Vue 2 响应式原理 (Reactivity)
Vue 2 的响应式系统核心由三个部分组成:Observer, Dep, Watcher。
1. 依赖收集 (Dep)
Dep 类专门用于管理依赖。每个响应式属性都有一个对应的 Dep 实例。
javascript
/* dep.js */
let activeWatcher = null; // 全局变量,指向当前正在计算的 Watcher
export class Dep {
constructor() {
this.subs = []; // 存放订阅者 (Watchers)
}
// 收集依赖
depend() {
if (activeWatcher) {
this.subs.push(activeWatcher);
}
}
// 通知更新
notify() {
this.subs.forEach(watcher => watcher.update());
}
}2. 数据劫持 (Observer)
使用 Object.defineProperty 将对象属性转换为 getter/setter。
javascript
/* index.js (简化版) */
import { Dep } from "./dep";
export function defineReactive(obj, key, value) {
const dep = new Dep(); // 为每个属性创建一个闭包中的 dep
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 读取时收集依赖
dep.depend();
return value;
},
set(newVal) {
if (newVal !== value) {
value = newVal;
// 修改时通知更新
dep.notify();
}
}
});
}3. 观察者 (Watcher)
Watcher 是连接数据与视图(或回调函数)的纽带。
javascript
/* watcher.js */
export class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
this.getter = typeof expOrFn === 'function' ? expOrFn : parsePath(expOrFn);
// 初始化时,触发一次 getter,从而触发依赖收集
this.value = this.get();
}
get() {
// 将当前 watcher 设为全局 activeWatcher
activeWatcher = this; // (注:实际源码中用 pushTarget(this))
let value = this.getter.call(this.vm, this.vm);
// 恢复
activeWatcher = null; // (注:实际源码中用 popTarget())
return value;
}
update() {
const value = this.get(); // 获取新值
const oldValue = this.value;
this.value = value;
this.cb.call(this.vm, value, oldValue); // 执行回调
}
}
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;
};
}