vue 响应式原理
Vue 的响应式原理是其核心机制之一,实现了数据与视图的自动同步
1. 数据劫持(Data Observation)
Vue 通过劫持数据的读写操作,监听数据变化。
Vue 2:基于 Object.defineProperty
- 对象属性劫持:
遍历对象的每个属性,通过 Object.defineProperty 重写 get 和 set 方法。
get:访问属性时,触发依赖收集(将当前 Watcher 添加到依赖列表)。
set:修改属性时,触发更新通知(通知依赖列表中的所有 Watcher 更新)。
function defineReactive(obj, key) {
let value = obj[key];
const dep = new Dep(); // 依赖管理器
Object.defineProperty(obj, key, {
get() {
if (Dep.target) { // 当前正在计算的 Watcher
dep.addSub(Dep.target); // 收集依赖
}
return value;
},
set(newVal) {
if (newVal === value) return;
value = newVal;
dep.notify(); // 通知更新
}
});
}
- 数组劫持:
重写数组的变异方法(如 push, pop, splice 等),在调用这些方法时手动触发更新。
对数组元素的变化(如 arr[0] = 1)无法直接监听,需通过 Vue.set 或 splice 处理。
Vue 3:基于 Proxy
使用 Proxy 代理整个对象,无需遍历属性,支持动态新增属性和数组索引修改。
拦截操作:
get:访问属性时收集依赖。
set:修改属性时触发更新。
deleteProperty:删除属性时触发更新。
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;
}
});
}
2. 依赖收集(Dependency Collection)
Dep 类(Vue 2):
每个响应式属性对应一个 Dep 实例,管理所有依赖它的 Watcher。
通过 Dep.target 静态属性标记当前正在计算的 Watcher。
Effect 函数(Vue 3):
使用 effect 函数包裹副作用(如组件的渲染函数)。
访问响应式数据时,自动建立依赖关系。
3. 更新触发(Update Trigger)
Watcher(Vue 2):
代表一个依赖,如组件的渲染函数、计算属性等。
数据变化时,Dep 通知所有关联的 Watcher 执行更新。
ReactiveEffect(Vue 3):
类似 Watcher,但更轻量级。
数据变化时,触发副作用函数重新执行。
4. 异步更新队列
Vue 将数据变化的更新操作放入异步队列。
多次数据修改合并为一次更新,避免重复渲染。
使用 nextTick 在 DOM 更新后执行回调。
关键对比:Vue 2 vs. Vue 3
特性 Vue 2 (Object.defineProperty) Vue 3 (Proxy)
对象监听 递归遍历属性,逐个劫持 直接代理整个对象
数组监听 重写变异方法,无法监听索引/长度 直接监听索引和 length 变化
新增/删除属性 需使用 Vue.set/Vue.delete 自动支持
性能 初始化时递归遍历,性能较低 按需劫持,性能更优
总结
核心思想:通过拦截数据的读写操作,收集依赖关系,数据变化时自动触发更新。
Vue 2 的局限:对数组和新增属性需要特殊处理,初始化性能开销大。
Vue 3 的优化:利用 Proxy 实现更全面、高效的数据监听,简化 API 使用。