【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 重写了数组方法(如 pushpopshiftunshiftsplicesortreverse),以在这些方法被调用时触发视图更新。

优点

  • 允许数组操作自动触发视图更新。

缺点

  • 通过索引直接修改数组项时( vm.items[index] = newValue),不能触发视图更新。Vue 不能检测到这种变化。使用 Vue 的响应式数组方法来触发更新,如 vm.items.splice(index, 1, newValue)
  • 需要手动添加新的数组方法到 Vue 的响应式系统中,如果使用了未被重写的方法(如 filterconcat 和 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 可拦截到对象的 definePropertydeleteProperty 和 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 响应式系统的优点

  1. 更全面的响应式:能够拦截对象属性的添加、删除和修改,以及数组索引的修改,解决了 Vue 2.x 中的一些限制。
  2. 更好的性能:由于 Proxy 可以拦截更多类型的操作,Vue 3.x 的响应式系统在某些情况下可以更加高效地处理数据变化。
  3. 更简洁的代码:使用 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)
	}
})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值