【VUE】1.vue2与vue3响应式原理
vue2.x的响应式
Vue 2.x 的响应式系统是其核心特性之一,它允许 Vue 应用程序中的数据变化时,视图能够自动更新。这一特性主要通过 Object.defineProperty()
方法实现(对于对象)和通过重写数组方法(对于数组)来实现
对象类型的响应式实现
Vue 使用 Object.defineProperty()
将每个属性转换成 getter/setter。当访问或修改这些属性时,getter/setter 会被调用,从而允许 Vue 拦截属性的访问和修改(数据劫持),并在必要时更新视图。
优点:
- 精确控制属性访问和修改。
- 易于理解和实现。
缺点:
- 不能监听属性的新增或删除,界面不会更新。
如果需要为对象添加新属性,并希望它是响应式的,需要使用 Vue 提供的 Vue.set(object, propertyName, value)
方法或 this.$set(object, propertyName, value)
实例方法。
对于删除属性,可以使用 Vue.delete(object, propertyName)
或 this.$delete(object, propertyName)
来确保删除操作也能触发视图更新。
- 递归遍历对象属性可能导致性能问题(尽管 Vue 提供了选项来限制深度)。
数组类型的响应式实现
Vue 不能使用 Object.defineProperty()
拦截索引的访问,因为数组索引是特殊的对象键(数字),且数组的 length 属性也是一个特殊属性。Vue 重写了数组方法(如 push
、pop
、shift
、unshift
、splice
、sort
、reverse
),以在这些方法被调用时触发视图更新。
优点:
- 允许数组操作自动触发视图更新。
缺点:
- 通过索引直接修改数组项时(
vm.items[index] = newValue
),不能触发视图更新。Vue 不能检测到这种变化。使用 Vue 的响应式数组方法来触发更新,如vm.items.splice(index, 1, newValue)
。 - 需要手动添加新的数组方法到 Vue 的响应式系统中,如果使用了未被重写的方法(如
filter
、concat
和slice
),它们返回的数组将不是响应式的。
// 数据劫持
Object.defineProperty(data,'count'{
get () {},
set () {}
})
vue3.x的响应式
Vue 3.x 的响应式系统相较于 Vue 2.x 有了显著的改进,主要引入了 Proxy
和 Reflect
来实现更加全面和高效的响应式能力。
Proxy(代理)
Proxy:Proxy - JavaScript | MDN
Vue 3.x 使用 Proxy
来代替 Vue 2.x 中的 Object.defineProperty()
。Proxy
可在目标对象前架设一层“拦截”,外界对该对象的访问都必须先通过这层拦截,因此提供了一种对对象访问进行细粒度控制的能力。
- 属性值的读写:通过拦截
get
和set
操作符,Vue 可以追踪到对象属性的读取和修改,从而触发依赖更新。 - 属性的添加和删除:
Proxy
可拦截到对象的defineProperty
、deleteProperty
和ownKeys
等操作,因此 Vue 能够检测到新属性的添加和现有属性的删除,并相应地更新依赖。
Reflect(反射)
Reflect: Reflect - JavaScript | MDN
在 Vue 3.x 的响应式系统中,Reflect
被用来提供默认行为,对源对象属性进行操作。Reflect
是一个内置对象,提供了一套用于操作对象的方法,这些方法与 Object
的方法相同,但 Reflect
的方法总是返回结果,而 Object
的方法可能会抛出异常(如 Object.defineProperty()
在属性不可配置时)。
- 使用
Reflect
可以让Proxy
的处理逻辑更加清晰和一致。例如,在Proxy
的set
陷阱中,你可以使用Reflect.set()
来执行实际的属性设置操作,这样即使遇到异常情况,处理逻辑也会更加统一。
Vue 3.x 响应式系统的优点
- 更全面的响应式:能够拦截对象属性的添加、删除和修改,以及数组索引的修改,解决了 Vue 2.x 中的一些限制。
- 更好的性能:由于
Proxy
可以拦截更多类型的操作,Vue 3.x 的响应式系统在某些情况下可以更加高效地处理数据变化。 - 更简洁的代码:使用
Proxy
和Reflect
可以让响应式系统的实现更加简洁和直观。
注意事项
Proxy
和Reflect
是 ES6 引入的新特性,因此在一些旧版浏览器中可能不可用。Vue 3.x 需要在一个支持这些特性的环境中运行。- 虽然
Proxy
提供了强大的拦截能力,但也需要谨慎使用,以避免不必要的性能开销。在 Vue 3.x 中,Vue 团队已经对响应式系统的性能进行了优化,以确保其在实际应用中的高效性。
// 模拟Vue3中实现响应式
const p = new Proxy(obj, {
// 拦截读取属性值:读取obj的某个属性时调用
get(target,propName){
console.log(`读取了obj身上的${propName}属性`)
return Reflect.get(target,propName)
},
// 拦截设置属性值或添加新属性:修改obj的某个属性、或给obj追加某个属性时调用
set(target,propName,value){
console.log(`修改了obj身上的${propName}属性,我要去更新界面了!`)
Reflect.set(target,propName,value)
},
// 拦截删除属性:删除obj的某个属性时调用
deleteProperty(target,propName){
console.log(`删除了obj身上的${propName}属性,我要去更新界面了!)`
return Reflect.deleteProperty(target,propName)
}
})
程序员小羊!: 常不错的技术领域文章分享,解决了我在实践中的大问题!博主很有耐心,更有对知识的热忱和热爱,写了这么实用有效的分享,值得收藏点赞,我也写了相关领域一些文章,大佬可以指导下,大家可以互相关注,共同学习。
CSDN-Ada助手: Vue入门 技能树或许可以帮到你:https://edu.csdn.net/skill/vue?utm_source=AI_act_vue