vue2的响应式原理
- 属性描述符,也就是对对象的属性进行劫持
通过
get
收集依赖(收集使用该数据的组件),set
派发更新(通知依赖的组件重新渲染)- 依赖收集:每个组件实例对应一个 watcher对象,当组件渲染访问数据时,会触发属性的getter,将当前的watcher添加到依赖列表中(dep)
- 派发更新: 当数据变化时触发setter,Dep会通知所关联的watcher重新计算从而触发组件重新渲染
注意:数组的响应式 是通过重写数组的方法(push、pop、splice)来实现对数组变化的监听,因为这些方法无法通过
object.defineProperty
直接监听的
Object.defineProperty(obj,'a',{
value:10,
writable:false,// 不可重写
enumerable:false,// 不可遍历
configurable:false,// 不可修改值本身
})
// vue 响应式简化版实现
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) { // Dep.target 是当前 Watcher
dep.addSub(Dep.target); // 收集依赖
}
return val;
},
set(newVal) {
val = newVal;
dep.notify(); // 通知所有 Watcher 更新
},
});
}
vue3的响应式原理
vue 3
使用Proxy代替Object.defineProperty
, 解决了vue2的局限性,(如无法检查对象属性的新增和删除,数组索引的直接赋值等)提高了性能Proxy he reflect 的区别
- Proxy:可以拦截对象的整个操作(读写,增删)
- reflect 只能操作对象
响应式API : reactive 和 ref
- reactive(obj):将普通对象转换为响应式对象(通过 Proxy 代理)
- ref(value):将基本类型值包装为响应式对象(通过 .value 访问)
依赖和收集
- track: 再getter中追踪依赖(类似vue2的dep)
- trigger: 在setter 中触发依赖
// 简化版实现
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key); // 收集依赖
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return true;
},
});
}
Vue 2 vs. Vue 3 响应式对比
特性 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |
---|---|---|
对象属性监听 | 需要遍历属性递归劫持 | 直接代理整个对象 |
数组监听 | 重写数组方法 | 直接监听索引变化 |
新增/删除属性 | 需手动调用 Vue.set/Vue.delete | 自动支持 |
性能 | 递归劫持有性能损耗 | 惰性劫持,按需触发 |