前端必读: Vue 响应式系统大 PK - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX    推广

前端必读: Vue 响应式系统大 PK

  •  
  •   GrapeCityChina 2021-05-19 11:55:11 +08:00 1302 次点击
    这是一个创建于 1607 天前的主题,其中的信息可能已经有所发展或是发生改变。

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

    响应式系统( Reactivity systems )是现代前端框架的关键部分之一。应用系统的的高度交互性、动态性和响应能力全靠它支持。每个 Web 开发人员而言都应该了解这一系统的功能和实践操作。

    原理

    响应系统是一种使自动使数据源(模型)与数据表示(视图)层自动保持同步的机制。每次模型更改时,都会重新渲染视图。

    以一个简单的 Markdown 编辑器为例。通常编辑器有两个窗格:一个窗格用于编写 Markdown 代码(用于修改基础模型),另一个窗格用于预览已编译的 HTML (显示已更新的视图)。当我们在书写窗格中写东西时,它会立即在预览窗格中自动预览。这个例子比较简单,在实际情况中会复杂很多。

    在许多情况下,我们要显示的数据取决于其他数据。在这种情况下,需要跟踪相关数据,并根据跟踪情况来更新数据。例如,我们有一个 fullName,该属性由 firstName 和 lastName 属性组成。修改其任何依赖项后,fullName 将自动重新评估,并在视图中显示结果。

    了解什么是响应式系统后,在了解 Vue 3 中的响应系统如何工作以及如何在实践中使用之前,让我们一起来快速回顾一下 Vue 2 中的响应系统内容及其注意事项。

    Vue 2 的响应式系统简介

    Vue 2 中的响应或多或少会被“隐藏”。无论我们放置在 data 对象中的是什么,Vue 都会使其隐式反应( reactive implicitly )。这样虽然可以使开发人员的工作更加轻松,但灵活度却会不可避免的降低。 在幕后,Vue 2 使用 ES5 Object.defineProperty 将 data 对象的所有属性转换为 getter 和 setter 。对于每个组件实例,Vue 创建一个依赖关系观察程序实例,观察者会记录组件渲染期间依赖收集 /跟踪的任何属性。当属性触发依赖项的设置器时,将通知观察者,并将组件重新渲染并更新视图。但是却也会有一些问题存在。

    变更检测警告

    由于 Object.defineProperty 方法的限制,Vue 无法检测到某些数据更改。包括:

    • 给对象添加属性或把对象移除属性(例如 obj.newKey = value )
    • 按索引设置数组项(例如 arr[index] = newValue )
    • 修改数组的长度(例如 arr.length = newLength ) 不过为了解决这些问题,Vue 为提供了 Vue.set API 方法,该方法向响应对象添加了一个属性,确保新属性也是响应性的,从而触发了视图更新。

    用下述实例讨论该情况:

     <h1>Hello! My name is {{ person.name }}. I'm {{ person.age }} years old.</h1> <button @click="addAgeProperty">Add "age" property</button> <p>Here are my favorite activities:</p> <ul> <li v-for="item, index in activities" :key="index"> {{ item }} <button @click="editActivity(index)">Edit</button> </li> </ul> <button @click="clearActivities">Clear the activities list</button> </div> 
     el: '#app', data: { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] }, methods: { // 1. Add a new property to an object addAgeProperty() { this.person.age = 30 }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { this.activities[index] = newValue } }, // 3. Modifying the length of the array clearActivities() { this.activities.length = 0 } } }); 

    在上面的示例中,我们会发现这三种方法都不起作用。我们不能向该 person 对象添加新属性,无法使用 activities 的索引来编辑数组中的项目,也不能修改 activities 数组的长度。

    优化如下:

     el: '#app', data: { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] }, methods: { // 1. Adding a new property to the object addAgeProperty() { Vue.set(this.person, 'age', 30) }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { Vue.set(this.activities, index, newValue) } }, // 3. Modifying the length of the array clearActivities() { this.activities.splice(0) } } }); 

    在此示例中,我们用 Vue.setAPI 方法将新 age 属性添加到 person 对象,并从活动数组中选择 /修改特定项目。在最后一种情况下,使用 Javascript 内置 splice 方法。

    这个做法完全可行但却略显笨拙,而且会导致前后代码不一致。而 Vue 3 就解决了这个问题。 我们用下面示例继续看:

     data() { return { person: { name: "David" }, activities: [ "Reading books", "Listening music", "Watching TV" ] } }, methods: { // 1. Adding a new property to the object addAgeProperty() { this.person.age = 30 }, // 2. Setting an array item by index editActivity(index) { const newValue = prompt('Input a new value') if (newValue) { this.activities[index] = newValue } }, // 3. Modifying the length of the array clearActivities() { this.activities.length = 0 } } } Vue.createApp(App).mount('#app') 

    可以看到在 Vue 3 中,所有方法都可以正常工作。

    在 Vue 2.6 中,引入的 Vue.observable API 方法,一定程度的公开了响应式系统,使开发人员可以体验到响应式系统的内容。实际上,这和 Vue 内部用来包装 data 对象是完全相同的方法,对于在简单场景创建小的跨组件状态存储很有用。但依旧没办法和 Vue3 的响应式系统相比,接下来就为大家详细介绍。

    注意:由于 Object.defineProperty 方法是仅限 ES5 且不可调整的功能,因此 Vue 2 不支持 IE8 及以下版本。

    Vue 3 响应式系统如何工作

    为了充分利用 ES6 Proxy and Reflect API,Vue 3 中的响应式系统已被完全重写。新版本新增响应式 API,该 API 使系统比以前更加灵活和强大。

    Proxy API 允许开发人员拦截和修改目标对象上的更低级对象操作。代理( proxy )是对象的克隆 /包装( clone/wrapper ),并提供特殊功能(称为 target ),这些功能响应特定的操作并覆盖 Javascript 对象的内置行为(称为 traps )。如果仍然需要使用默认行为,则可以使用相应的 Reflection API,其名称顾名思义就是反映 Proxy API 的方法。这里有一个示例,用来了解如何在 Vue 3 中使用这些 API:

     name: "David", age: 27 }; const handler = { get(target, property, receiver) { // track(target, property) console.log(property) // output: name return Reflect.get(target, property, receiver) }, set(target, property, value, receiver) { // trigger(target, property) console.log(`${property}: ${value}`) // output: "age: 30" and "hobby: Programming" return Reflect.set(target, property, value, receiver) } } let proxy = new Proxy(person, handler); console.log(person) // get (reading a property value) console.log(proxy.name) // output: David // set (writing to a property) proxy.age = 30; // set (creating a new property) proxy.hobby = "Programming"; console.log(person) 

    要创建一个新的代理,使用 new Proxy(target, handler)构造函数。它带有两个参数:目标对象( person 对象)和处理程序对象,该对象定义将拦截哪些操作( get 和 set 操作)。在 handler 对象中,get 和 set 陷阱来跟踪何时读取属性以及何时修改 /添加属性。设置控制台语句以确保运行正确。

    在 get 和 set 陷阱采用下列参数:

    • target:代理包装的目标对象
    • property:属性名称
    • value:属性值(此参数仅用于设置操作)
    • receiver:进行操作的对象(通常是代理)

    Reflect API 方法与其相应的代理方法接受相同的参数

    注释中 track 函数和 trigger 函数特定用于 Vue,用于跟踪何时读取属性以及何时修改 /添加属性。

    在示例的最后一部分,用控制台语句输出原始 person 对象。然后用另一份声明中读取属性 name 的 proxy 对象。接下来,修改 age 属性并创建一个新 hobby 属性。最后,再次输出该对象以查看它是否正确更新。

    以上就是 Vue3 响应式系统的完整工作流程,但在实际工作中会复杂得多。

    使用 Vue 3 响应式系统,还有一些注意事项:

    • 仅适用于支持 ES6 +的浏览器
    • 响应代理不等于原始对象

    总结

    以上我们将 Vue2 和 Vue3 中响应式系统部分进行了比较,并对响应式系统的工作原理进行了说明,在后面的文章中,我们会进一步为大家介绍 Vue3 中响应式系统的 API,敬请期待。

    GrapeCityChina
        1
    GrapeCityChina  
    OP
       2021-05-20 15:06:55 +08:00
    扩展阅读:

    如果你已经熟练掌握 Vue3, 不妨让我们通过实际搭建一款基于 Vue 3 组件的表格编辑系统( https://www.grapecity.com.cn/developer/spreadjs/vue ),更加深入学习
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2579 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 04:57 PVG 12:57 LAX 21:57 JFK 00:57
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86