Vue 2中defineProperty实现响应式系统原理
- 数据劫持:通过
Object.defineProperty()
方法,对对象的每个属性进行数据劫持。例如:
let data = { name: 'John' };
Object.keys(data).forEach(key => {
let value = data[key];
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
return value;
},
set(newValue) {
if (newValue!== value) {
value = newValue;
// 通知视图更新
console.log('属性值更新,通知视图更新');
}
}
});
});
- 依赖收集:在
get
方法中收集依赖,通常是通过一个Dep
类来管理依赖。每个属性对应一个Dep
实例,当属性被访问时,将当前的Watcher
实例添加到Dep
中。
- 派发更新:在
set
方法中,当属性值变化时,遍历Dep
中收集的依赖(Watcher
实例),调用Watcher
的更新方法,通知视图更新。
Vue 3中Proxy实现响应式系统原理
- 数据劫持:使用
Proxy
对象创建一个代理,拦截对象的操作。例如:
let data = { name: 'John' };
let proxy = new Proxy(data, {
get(target, property) {
// 依赖收集
console.log('属性被访问,进行依赖收集');
return target[property];
},
set(target, property, value) {
if (target[property]!== value) {
target[property] = value;
// 通知视图更新
console.log('属性值更新,通知视图更新');
return true;
}
return false;
}
});
- 依赖收集:在
get
拦截函数中进行依赖收集,与Vue 2类似,但实现方式有所不同。
- 派发更新:在
set
拦截函数中,当属性值变化时,通知相关依赖进行更新。
二者区别
- 使用场景
- Vue 2(defineProperty):对于已有对象的属性劫持比较方便,但对于新增属性和删除属性需要特殊处理(使用
Vue.set
和Vue.delete
)。
- Vue 3(Proxy):对整个对象进行代理,新增和删除属性不需要特殊处理,更适合复杂数据结构的响应式处理。
- 性能
- Vue 2(defineProperty):在初始化时,对每个属性进行遍历劫持,对于大型对象性能开销较大。并且在新增和删除属性时,也会有额外性能开销。
- Vue 3(Proxy):初始化时只创建一个代理,性能相对较好。而且由于对新增和删除属性的处理更自然,在操作对象结构时性能也更优。
- 数据劫持的粒度
- Vue 2(defineProperty):只能对对象的单个属性进行劫持。
- Vue 3(Proxy):可以对整个对象进行劫持,对对象内深层次的嵌套结构也能自然处理,数据劫持粒度更粗但更灵活。