实现vue中的watcher
Vue 中的 Watcher 实现
在 Vue 中,Watcher 是响应式系统的核心组件之一,用于监听数据变化并执行回调。以下是实现 Watcher 的关键方法和步骤。
创建 Watcher 类
Watcher 类需要接收 Vue 实例、要监听的表达式(或函数)以及回调函数。初始化时会进行依赖收集。
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
this.getter = parsePath(expOrFn); // 解析表达式为函数
this.value = this.get(); // 初始化时触发依赖收集
}
get() {
Dep.target = this; // 设置当前 Watcher 为全局目标
const value = this.getter.call(this.vm, this.vm); // 触发属性的 getter
Dep.target = null; // 收集完成后清除目标
return value;
}
update() {
const oldValue = this.value;
this.value = this.get(); // 重新获取值
this.cb.call(this.vm, this.value, oldValue); // 执行回调
}
}
解析表达式
parsePath 用于将字符串路径(如 'a.b.c')转换为访问对象属性的函数。
function parsePath(path) {
const segments = path.split('.');
return function (obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]];
}
return obj;
};
}
依赖收集与触发
依赖收集通过 Dep 类实现,每个响应式属性会关联一个 Dep 实例。
class Dep {
constructor() {
this.subs = []; // 存储 Watcher 实例
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update()); // 通知所有 Watcher 更新
}
}
// 在响应式属性的 getter 中收集依赖
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if (Dep.target) {
dep.addSub(Dep.target); // 将当前 Watcher 添加到依赖列表
}
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // 触发更新
}
});
}
示例使用
以下是如何在 Vue 实例中使用自定义 Watcher 监听数据变化。
const vm = { a: { b: 1 } };
defineReactive(vm.a, 'b', vm.a.b); // 使属性响应式
new Watcher(vm, 'a.b', (newVal, oldVal) => {
console.log(`值从 ${oldVal} 变为 ${newVal}`);
});
vm.a.b = 2; // 输出: "值从 1 变为 2"
注意事项
- 性能优化:避免过度使用 Watcher,复杂逻辑可改用计算属性。
- 深层监听:对于嵌套对象,需递归调用
defineReactive。 - 异步更新:Vue 实际实现中会将 Watcher 推入队列异步更新,避免重复计算。
通过以上步骤,可以实现 Vue 中 Watcher 的核心功能,监听数据变化并触发回调。







