Skip to content

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;
  };
}

MIT Licensed | Keep Learning.