Vue 2 的响应式系统通过对象劫持、依赖收集、和更新通知机制来实现数据驱动视图的更新。以下是具体实现的详细步骤:

1. 对象劫持(Object.defineProperty)

1.1. 定义响应式对象

使用 Object.defineProperty 对对象的每个属性进行劫持。Vue 2 会为每个属性定义 getter 和 setter,以便在属性被访问或修改时触发相应的逻辑。

function defineReactive(obj, key, val) {
  const dep = new Dep(); // 用于管理依赖

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      if (Dep.target) {
        dep.addSub(Dep.target); // 添加当前 watcher 作为依赖
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      dep.notify(); // 通知所有依赖更新
    }
  });
}

1.2. 递归劫持嵌套对象

如果属性的值是对象,Vue 2 需要递归地将这些对象转化为响应式对象。通过遍历对象的所有属性来实现。

function observe(value) {
  if (!value || typeof value !== 'object') return;
  return new Observer(value);
}

class Observer {
  constructor(value) {
    this.value = value;
    this.walk(value);
  }

  walk(obj) {
    Object.keys(obj).forEach(key => defineReactive(obj, key, obj[key]));
  }
}

2. 依赖收集与通知

2.1. 依赖管理(Dep 类)

Dep 类用于管理所有依赖(即 Watcher 实例)。每当一个属性被访问时,它会将当前 Watcher 实例添加到依赖列表中。当属性值变化时,Dep 会通知所有依赖进行更新。

class Dep {
  constructor() {
    this.subs = [];
  }

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

  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

2.2. Watcher 类

Watcher 类是 Vue 2 响应式系统的核心。它用于接收数据变化通知并更新视图。

class Watcher {
  constructor(updateFn) {
    this.update = updateFn;
    Dep.target = this;
    // 触发 getter 以便收集依赖
    this.value = this.get();
    Dep.target = null;
  }

  get() {
    // 访问数据属性触发 getter
  }

  update() {
    // 数据变化时的处理逻辑
    this.get();
  }
}

3. 计算属性与侦听器

3.1. 计算属性(Computed Properties)

计算属性是基于响应式数据的缓存值。Vue 2 会自动缓存计算属性的结果,直到它依赖的响应式数据发生变化。

computed: {
  doubledValue() {
    return this.value * 2;
  }
}

在实现上,计算属性会创建一个专门的 Watcher 实例,并在依赖的属性发生变化时重新计算其值。

3.2. 侦听器(Watchers)

侦听器用于观察数据变化并执行指定的回调函数。

watch: {
  value(newValue, oldValue) {
    // 当 value 发生变化时执行的逻辑
  }
}

4. 模板编译

Vue 2 的模板编译过程将模板转换成渲染函数,并利用响应式系统来实现数据驱动的视图更新。渲染函数会访问组件的数据属性,触发依赖收集和视图更新。

function compileTemplate(template) {
  // 编译模板为渲染函数
  return function render() {
    // 渲染逻辑
  };
}

在渲染过程中,模板中的数据绑定会触发属性的 getter,自动收集依赖。当数据变化时,Watcher 会重新计算并更新视图。

5. 响应式数据的访问与更新

5.1. 访问数据

当访问一个响应式属性时,getter 会被触发,当前的 Watcher 会被添加到依赖列表中。

function get() {
  // 访问数据属性触发 getter
  return this.value;
}

5.2. 更新数据

当设置响应式属性的值时,setter 会被触发,更新数据并通知所有依赖进行更新。

function set(newVal) {
  if (newVal !== this.value) {
    this.value = newVal;
    this.dep.notify(); // 通知所有依赖
  }
}

总结

  1. 对象劫持:通过 Object.defineProperty 劫持对象的属性,实现对属性的读写操作的拦截。
  2. 递归劫持:递归地将嵌套对象转化为响应式对象。
  3. 依赖管理Dep 类用于管理和通知依赖。
  4. 计算属性与侦听器:缓存计算属性,利用侦听器响应数据变化。
  5. 模板编译:将模板编译为渲染函数,自动更新视图。

这些细节使 Vue 2 能够高效地实现双向数据绑定和响应式更新,确保视图和数据的一致性。

本文由mdnice多平台发布


jywud
42 声望7 粉丝