Vue 的响应式原理是其核心机制之一,实现了数据与视图的自动同步

1. 数据劫持(Data Observation)

Vue 通过劫持数据的读写操作,监听数据变化。

Vue 2:基于 Object.defineProperty

  • 对象属性劫持:

遍历对象的每个属性,通过 Object.defineProperty 重写 get 和 set 方法。
get:访问属性时,触发依赖收集(将当前 Watcher 添加到依赖列表)。
set:修改属性时,触发更新通知(通知依赖列表中的所有 Watcher 更新)。

function defineReactive(obj, key) {
  let value = obj[key];
  const dep = new Dep(); // 依赖管理器
  
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) { // 当前正在计算的 Watcher
        dep.addSub(Dep.target); // 收集依赖
      }
      return value;
    },
    set(newVal) {
      if (newVal === value) return;
      value = newVal;
      dep.notify(); // 通知更新
    }
  });
}
  • 数组劫持:

重写数组的变异方法(如 push, pop, splice 等),在调用这些方法时手动触发更新。
对数组元素的变化(如 arr[0] = 1)无法直接监听,需通过 Vue.set 或 splice 处理。

Vue 3:基于 Proxy

使用 Proxy 代理整个对象,无需遍历属性,支持动态新增属性和数组索引修改。

拦截操作:

get:访问属性时收集依赖。
set:修改属性时触发更新。

deleteProperty:删除属性时触发更新。

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      track(target, key); // 收集依赖
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(target, key); // 触发更新
      return true;
    }
  });
}

2. 依赖收集(Dependency Collection)

Dep 类(Vue 2):

每个响应式属性对应一个 Dep 实例,管理所有依赖它的 Watcher。

通过 Dep.target 静态属性标记当前正在计算的 Watcher。

Effect 函数(Vue 3):

使用 effect 函数包裹副作用(如组件的渲染函数)。

访问响应式数据时,自动建立依赖关系。

3. 更新触发(Update Trigger)

Watcher(Vue 2):

代表一个依赖,如组件的渲染函数、计算属性等。

数据变化时,Dep 通知所有关联的 Watcher 执行更新。

ReactiveEffect(Vue 3):

类似 Watcher,但更轻量级。

数据变化时,触发副作用函数重新执行。

4. 异步更新队列

Vue 将数据变化的更新操作放入异步队列。

多次数据修改合并为一次更新,避免重复渲染。

使用 nextTick 在 DOM 更新后执行回调。

关键对比:Vue 2 vs. Vue 3

特性       Vue 2 (Object.defineProperty)           Vue 3 (Proxy)
对象监听       递归遍历属性,逐个劫持                直接代理整个对象
数组监听       重写变异方法,无法监听索引/长度        直接监听索引和 length 变化
新增/删除属性   需使用 Vue.set/Vue.delete           自动支持
性能       初始化时递归遍历,性能较低                按需劫持,性能更优

总结

核心思想:通过拦截数据的读写操作,收集依赖关系,数据变化时自动触发更新。

Vue 2 的局限:对数组和新增属性需要特殊处理,初始化性能开销大。

Vue 3 的优化:利用 Proxy 实现更全面、高效的数据监听,简化 API 使用。