Vue2中的响应式系统概述
简介Vue2的响应式系统
Vue2响应式系统的主要特点
1. 数据绑定:
Vue 2的响应式系统是该框架核心的一个重要部分,它使得数据和视图能够保持同步,实现了双向绑定。
2. Object.defineProperty的运用:
Vue是采用数据劫持 结合发布者-订阅者模式的方式,通过使用ES5中的Object.defineProperty()
方法来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
3. 侦听器(Watcher):
为了追踪数据的变化,引入了Watcher机制。Watcher会监听数据的变化,并在变化发生时通知相关的订阅者更新视图。
defineProperty的特点和局限性
defineProperty的工作原理
js
var obj = {};
// 在定义属性时,使用 Object.defineProperty 进行拦截
Object.defineProperty(obj, 'name', {
get: function() {
console.log('访问 name 属性');
return this._name;
},
set: function(value) {
console.log('设置 name 属性为:', value);
this._name = value;
}
});
// 访问属性会触发 getter
console.log(obj.name);
// 设置属性会触发 setter
obj.name = 'John';
Object.defineProperty
属性拦截原理:利用get
、set
这两个trap实现拦截
defineProperty在数组变化监测方面的局限性
Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。
js
var arr = [1, 2, 3];
// 以下操作无法触发响应式更新
arr[0] = 100;
上述代码中,通过直接修改数组的下标 arr[0]
,不会触发响应式更新,因为Object.defineProperty
无法拦截这种直接对数组元素的赋值操作。
Vue3的响应式系统介绍
Vue3与Vue2的响应式系统对比:
1. Proxy取代Object.defineProperty:
Vue3中使用Proxy对象来实现响应式数据,相较于Vue2的Object.defineProperty
,Proxy
能够更全面、直接地捕获对象的操作,包括读取、写入、删除、属性枚举等,使得响应式系统更为灵活。
2. 更好的数组响应式:
在Vue3中,数组的响应式处理得到显著改进。Proxy可以直接监听数组的操作,包括长度的变化和通过下标修改元素值,消除了Vue 2中处理数组变异的一些限制。
3. Refs和Reactive API:
引入了新的ref
和reactive
API,使得创建和管理响应式数据更为简单。ref
用于创建基本数据类型的响应式引用,而reactive
则用于创建对象或数组的响应式版本,提高了代码的可读性和可维护性。
4. Composables的引入:
Vue3将一些常用的响应式功能抽象成了可组合的函数,称为Composables。例如,computed
和watch
现在是独立的composables,可以更灵活地在组件中使用,提高了代码的模块化程度。
5. 动态添加响应式属性:
Vue3允许在运行时动态添加响应式属性,不再需要在对象创建时就定义好。这使得在运行时根据条件动态添加属性更为方便,增加了开发的灵活性。
Proxy的基本概念和优势
Proxy
是ECMAScript6
引入的一个新对象,用于创建一个代理对象,可以拦截并定义对象的基本操作。
Proxy是如何工作的
const p = new Proxy(target, handler)
参数
target
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
代码举例
js
// 定义一个需要代理的对象
let individual = {
age: 0,
university: 'XYZU'
}
// 定义代理处理器对象
let handler = {
get(obj, key) {
console.log('正在获取属性值')
// 如果对象中存在该属性,返回属性值;否则,返回默认值:99
return key in obj ? obj[key] : 99
},
set(obj, key, val) {
console.log('正在设置属性值')
obj[key] = val
return true
}
}
// 使用Proxy创建代理对象
let proxiedIndividual = new Proxy(individual, handler)
// 测试get方法是否成功拦截
console.log(proxiedIndividual.university) // 输出:正在获取属性值 XYZU
console.log(proxiedIndividual.name) // 输出:正在获取属性值 99
// 测试set方法是否成功拦截
proxiedIndividual.age = 25 // 输出:正在设置属性值
console.log(proxiedIndividual.age) // 输出:正在获取属性值 25
使用Proxy有什么优势
1. 解决了Vue2中因为使用defineProperty
导致的局限性
2. Vue2篡改了数组的7个API,Vue3用了Proxy就不需要了,性能更加的好
3. Proxy支持13种拦截操作
4. 数组中的优势
Proxy相对于 Object.defineProperty
在处理数组等特殊情况下具有显著的优势,主要体现在其更大的灵活性:
- 直接捕获数组操作: Proxy 可以直接捕获数组的各种操作,包括读取、写入、删除、枚举等,而不需要像
Object.defineProperty
一样通过对数组原型方法的重写来实现。这使得对数组的操作更为直观和精确。 - 动态处理数组变异方法: Proxy 能够动态捕获数组的变异方法(如
push
、pop
、shift
等),而不需要事先定义。这与 Vue2 中需要对这些方法进行重写的方式相比,更加灵活和自然。 - 对数组长度的动态响应: 使用Proxy可以直接捕获数组长度的变化,而不需要额外的特殊处理。在Vue2中,处理数组长度变化需要通过额外的逻辑来实现,而Proxy 可以更直接地实现这种响应式。
- 无需递归设置: Proxy 不需要递归设置,可以自动处理嵌套数组的情况,而
Object.defineProperty
需要显式地递归设置对象属性。这减轻了初始化时的性能开销,尤其是在处理大型嵌套数组时。 - 更直观的语法: 使用 Proxy 可以更直观地表达响应式行为,而不需要像
Object.defineProperty
那样在对象初始化时定义每个属性的响应式规则。这使得代码更易读、易理解。
Proxy性能优势:
由于 Proxy
直接拦截操作,而不需要递归设置,因此在性能上相较于 Object.defineProperty
有一定的优势,特别是在大型项目中或者对数组进行频繁变更时。
总结
在 Vue 3 中选择 Proxy 作为响应式系统核心,主要是为了提供更灵活、高效和直观的响应式实现,满足复杂应用场景的需求。