目录

vue2双向绑定解析

vue2双向绑定解析

Vue 2 的双向绑定原理基于 Object.defineProperty ,核心源码在 src/core/observer 目录中。

1. 核心模块: observer

observer 模块负责将普通对象转换为响应式对象,主要包括以下文件:

  • index.js :定义 Observer 类,用于将对象转换为响应式。
  • dep.js :定义 Dep 类,用于管理依赖(订阅者)。
  • watcher.js :定义 Watcher 类,用于监听数据变化并触发更新。

2. Observer

Observer 类是 Vue 2 响应式系统的核心,它通过 Object.defineProperty 将对象的属性转换为 gettersetter ,从而实现依赖收集和派发更新。

源码位置: src/core/observer/index.js

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number;

  constructor(value: any) {
    this.value = value;
    this.dep = new Dep();
    this.vmCount = 0;
    def(value, '__ob__', this); // 将 Observer 实例挂载到对象的 __ob__ 属性上
    if (Array.isArray(value)) {
      // 处理数组
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      this.observeArray(value);
    } else {
      // 处理对象
      this.walk(value);
    }
  }

  walk(obj: Object) {
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i]);
    }
  }

  observeArray(items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  }
}
  • Observer 类会递归地将对象的属性转换为响应式。
  • 对于数组,Vue 2 通过重写数组的变异方法(如 pushpop 等)来实现响应式。

3. defineReactive 函数

defineReactive 是 Vue 2 实现响应式的核心函数,它通过 Object.defineProperty 定义属性的 gettersetter

源码位置: src/core/observer/index.js

export function defineReactive(
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep(); // 每个属性都有一个 Dep 实例,用于管理依赖

  const getter = property && property.get;
  const setter = property && property.set;
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key];
  }

  let childOb = !shallow && observe(val); // 递归处理嵌套对象
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend(); // 收集依赖
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set: function reactiveSetter(newVal) {
      const value = getter ? getter.call(obj) : val;
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return;
      }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal); // 对新值进行响应式处理
      dep.notify(); // 通知依赖更新
    },
  });
}
  • getter :在访问属性时,调用 dep.depend() 收集依赖。
  • setter :在修改属性时,调用 dep.notify() 通知依赖更新。

4. Dep

Dep 类是依赖管理器,用于存储和管理 Watcher 实例。

源码位置: src/core/observer/dep.js

export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub: Watcher) {
    this.subs.push(sub);
  }

  removeSub(sub: Watcher) {
    remove(this.subs, sub);
  }

  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }

  notify() {
    const subs = this.subs.slice();
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  }
}
  • Dep.target :当前正在计算的 Watcher 实例。
  • subs :存储所有订阅了该属性的 Watcher 实例。
  • notify :通知所有订阅者更新。

5. Watcher

Watcher 类是 Vue 2 中用于监听数据变化的订阅者,它会在数据变化时触发回调函数。

源码位置: src/core/observer/watcher.js

export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  getter: Function;
  value: any;

  constructor(
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm;
    this.cb = cb;
    this.id = ++uid; // uid for batching
    this.deps = [];
    this.newDeps = [];
    this.depIds = new Set();
    this.newDepIds = new Set();
    this.getter = parsePath(expOrFn);
    this.value = this.get();
  }

  get() {
    pushTarget(this); // 将当前 Watcher 设置为 Dep.target
    let value;
    const vm = this.vm;
    try {
      value = this.getter.call(vm, vm);
    } catch (e) {
      // 处理错误
    } finally {
      popTarget(); // 恢复之前的 Watcher
      this.cleanupDeps();
    }
    return value;
  }

  update() {
    queueWatcher(this); // 将 Watcher 加入队列,等待批量更新
  }

  run() {
    const value = this.get();
    if (value !== this.value || isObject(value)) {
      const oldValue = this.value;
      this.value = value;
      this.cb.call(this.vm, value, oldValue); // 执行回调
    }
  }
}
  • Watcher 实例会在初始化时调用 get 方法,触发属性的 getter ,从而收集依赖。
  • 当数据变化时, Watcherupdate 方法会被调用,最终触发回调函数。

Vue 2 的双向绑定原理基于 Object.defineProperty ,通过以下步骤实现:

  1. 响应式化Observer 类将对象的属性转换为 gettersetter
  2. 依赖收集 :在 getter 中调用 dep.depend() ,将当前 Watcher 添加到依赖列表中。
  3. 派发更新 :在 setter 中调用 dep.notify() ,通知所有依赖的 Watcher 更新。
  4. 批量更新Watcher 的更新会被加入队列,异步执行,以提高性能。