Vue数据依赖实现
Vue数据依赖实现原理
Vue的数据依赖实现主要基于响应式系统和依赖收集机制,核心是通过Object.defineProperty或Proxy拦截数据变化,并在getter/setter中建立依赖关系。
核心流程
响应式初始化
通过Observer类遍历对象属性,使用Object.defineProperty将其转换为getter/setter。对于数组,会重写原型方法(如push/pop)来触发更新。
依赖收集
在getter中调用dep.depend(),将当前Watcher(组件渲染函数或计算属性)添加到依赖列表。每个属性对应一个Dep实例管理所有依赖它的Watcher。
派发更新
当数据变化时触发setter,调用dep.notify()通知所有依赖的Watcher执行更新。Watcher会将自身加入队列,通过nextTick批量执行。
关键代码示例
// 简化版Observer
class Observer {
constructor(value) {
this.walk(value);
}
walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
// 定义响应式属性
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) dep.depend(); // 收集依赖
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 触发更新
}
});
}
// 依赖管理器
class Dep {
constructor() {
this.subs = [];
}
depend() {
this.subs.push(Dep.target);
}
notify() {
this.subs.forEach(watcher => watcher.update());
}
}
优化机制
异步更新队列
通过nextTick将多个数据变更合并为一次DOM更新,避免重复渲染。使用Promise/MutationObserver/setTimeout实现降级兼容。
虚拟DOM Diff
组件级更新时通过虚拟DOM对比最小化DOM操作,配合key属性优化列表渲染性能。
计算属性缓存
计算属性会根据依赖是否变化决定重新计算或返回缓存值,通过dirty标志位控制。
Vue 3改进
Vue 3改用Proxy实现响应式,解决了Vue 2中无法检测新增属性、数组长度变化等限制。依赖收集通过track函数,更新触发通过trigger函数,与ReactiveEffect配合实现更细粒度的控制。







