【vue篇】Vue 响应式核心:依赖收集机制深度解密

在 Vue 应用中,你是否好奇:

"当我修改 this.message 时,DOM 为何能自动更新?" "为什么只有被模板用到的数据才会触发更新?" "Vue 是如何知道哪个组件依赖哪个数据的?"

这一切的背后,是 Vue 依赖收集(Dependency Collection) 的精妙设计。

本文将从 Object.definePropertyDep-Watcher 模型,彻底解析 Vue 2 的响应式原理。


一、核心结论:依赖收集 = 数据 ↔ 视图 的双向绑定

text 复制代码
数据变化 → 通知视图更新
     ↑          ↓
   收集      触发 getter
  • 谁收集? Dep(依赖中心)
  • 被谁收集? Watcher(观察者)
  • 何时收集? 组件渲染时读取数据(触发 getter)

二、三大核心角色

🎯 1. defineReactive:让数据"响应式"

js 复制代码
function defineReactive(obj, key, val) {
  // 每个属性都有一个独立的依赖中心
  const dep = new Dep();
  
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      // ✅ 依赖收集:谁在读我?
      if (Dep.target) {
        dep.depend(); // 通知 dep:当前 watcher 依赖我
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      // ✅ 派发更新:通知所有依赖者
      dep.notify();
    }
  });
}

💥 dep 是每个属性的"私人秘书",记录谁依赖它。


🎯 2. Dep:依赖管理中心

js 复制代码
class Dep {
  static target = null; // 🌟 全局唯一,指向当前正在计算的 Watcher
  subs = []; // 存储所有依赖此数据的 Watcher

  // 被收集:当前 Watcher 依赖我
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this); // 告诉 Watcher:你依赖我
    }
  }

  // 添加订阅者
  addSub(watcher) {
    this.subs.push(watcher);
  }

  // 派发更新:数据变了!
  notify() {
    // 避免在 notify 时修改数组
    const subs = this.subs.slice();
    for (let i = 0; i < subs.length; i++) {
      subs[i].update(); // 通知每个 Watcher 更新
    }
  }
}

🔑 Dep.target 是关键:它确保同一时间只有一个 Watcher 在收集依赖。


🎯 3. Watcher:观察者(组件/计算属性)

js 复制代码
class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.getter = expOrFn; // 如 vm._update(vm._render())
    this.cb = cb;
    this.deps = [];      // 记录依赖了哪些 dep
    this.depIds = new Set(); // 去重
    this.value = this.get(); // 🚀 首次执行,触发依赖收集
  }

  // 读取数据,触发 getter
  get() {
    pushTarget(this); // 设置当前 Watcher
    const value = this.getter.call(this.vm, this.vm);
    popTarget(); // 清除
    return value;
  }

  // 被 dep 收集
  addDep(dep) {
    const id = dep.id;
    if (!this.depIds.has(id)) {
      this.depIds.add(id);
      this.deps.push(dep);
      dep.addSub(this); // dep 记录我
    }
  }

  // 更新:数据变化后调用
  update() {
    queueWatcher(this); // 异步更新
  }

  run() {
    const value = this.get(); // 重新计算
    this.cb(value, this.value); // 执行回调(如更新 DOM)
    this.value = value;
  }
}

💡 Watcher 是"消费者",它知道自己依赖哪些数据。


三、依赖收集全过程(图文解析)

🔄 阶段 1:初始化响应式数据

js 复制代码
// data: { message: 'Hello' }
defineReactive(data, 'message', 'Hello');
// → 为 message 创建 dep 实例
text 复制代码
data.message
     ↓
   dep (subs: [])

🔄 阶段 2:组件挂载,创建 Watcher

js 复制代码
new Watcher(vm, () => {
  vm._update(vm._render());
});
  • Watcher.get() 被调用;
  • pushTarget(this)Dep.target = watcher
text 复制代码
Dep.target → Watcher实例

🔄 阶段 3:渲染触发 getter,完成收集

js 复制代码
vm._render(); // 生成 VNode
// 模板中:{{ message }}
// → 读取 this.message → 触发 getter
js 复制代码
// getter 执行
get() {
  if (Dep.target) {
    dep.depend(); // dep.depend()
  }
  return val;
}
js 复制代码
// dep.depend()
depend() {
  Dep.target.addDep(this); // watcher.addDep(dep)
}
js 复制代码
// watcher.addDep(dep)
addDep(dep) {
  this.deps.push(dep);
  dep.addSub(this); // dep.subs.push(watcher)
}

收集完成!

text 复制代码
data.message
     ↓
   dep (subs: [watcher])
     ↑
Watcher (deps: [dep])

🔄 阶段 4:数据变化,派发更新

js 复制代码
this.message = 'World'; // 触发 setter
js 复制代码
// setter 执行
set(newVal) {
  val = newVal;
  dep.notify(); // 通知所有 subs
}
js 复制代码
// dep.notify()
notify() {
  this.subs.forEach(watcher => watcher.update());
}

queueWatcher(watcher) → 异步更新 DOM。


四、实战演示:一个简单的响应式系统

js 复制代码
// 1. 数据
const data = { count: 0 };

// 2. 响应式化
defineReactive(data, 'count', 0);

// 3. 创建 Watcher(模拟组件)
new Watcher(null, () => {
  console.log('Render:', data.count);
}, null);
// → 触发 getter → 依赖收集完成

// 4. 修改数据
data.count = 1;
// → 触发 setter → dep.notify() → Watcher.update()
// → 输出:Render: 1

五、Vue 3 的改进:Proxy + effect

js 复制代码
// Vue 3 使用 Proxy
const reactiveData = reactive({ count: 0 });

effect(() => {
  console.log(reactiveData.count); // 收集依赖
});

reactiveData.count++; // 触发更新
  • 优势
    • 支持动态新增属性;
    • 性能更好(无需递归 defineProperty);
    • 代码更简洁。

💡 结语

"依赖收集是 Vue 响应式的灵魂。"

角色 职责
defineReactive 拦截 getter/setter
Dep 管理订阅者(Watcher)
Watcher 观察数据变化,执行更新
过程 关键操作
初始化 defineReactive
收集 Dep.target = watcher + dep.depend()
更新 dep.notify()watcher.update()

掌握依赖收集机制,你就能:

✅ 理解 Vue 响应式原理;

✅ 调试响应式问题;

✅ 设计自己的响应式系统;

✅ 顺利过渡到 Vue 3 的 reactiveeffect

相关推荐
恋猫de小郭12 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅18 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606119 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了19 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅19 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅20 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅20 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment20 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅20 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊20 小时前
jwt介绍
前端