内容提要
Vue 响应式原理中,Dep 代表依赖,Watcher 是订阅者(来自 watch、computed、渲染函数)。数据 getter 时通过 Dep.target 建立 Dep 与 Watcher 的多对多关系;setter 时调用 dep.notify 通知订阅者更新,触发视图刷新。Observer 递归处理响应式对象,核心在 defineReactive 中实现。
延伸解读
Dep 与 Watcher 的多对多关系设计
Dep 和 Watcher 互相记录,形成多对多关系。Dep 的 subs 数组保存所有订阅该依赖的 Watcher,Watcher 的 deps 数组保存自身订阅的所有 Dep。这种双向记录使得当一方被清除时,能及时更新相关对象,避免内存泄漏和无效通知。例如,当 Watcher 被销毁时,会从所有关联的 Dep 中移除自己;当 Dep 不再需要时,也能通知 Watcher 移除引用。这种设计是 Vue 响应式系统高效运作的基础。
Dep.target 与依赖收集的巧妙机制
Vue 通过 Dep.target 这个全局变量来建立依赖关系。在 Watcher 执行回调前,会将当前 Watcher 赋值给 Dep.target;回调中访问响应式数据时,触发 getter,getter 检查 Dep.target 存在则调用 dep.depend(),将当前 Watcher 添加到 Dep 的订阅列表。这样,Dep 就能准确知道哪些 Watcher 依赖自己。当数据变化时,setter 调用 dep.notify() 通知所有订阅者更新。这种机制实现了自动依赖收集,无需手动声明依赖。
Watcher 的三种类型与更新流程
Watcher 有三种:watch、computed 和渲染函数。它们都在数据变化时触发更新,但行为不同。渲染 Watcher 负责视图更新,computed Watcher 具有 lazy 属性,数据变化时仅标记 dirty,下次访问时重新计算;watch Watcher 则执行用户定义的回调。更新时,Watcher.update 根据 lazy 和 sync 决定立即运行还是加入队列异步更新。最终,Watcher.run 执行回调,对于渲染 Watcher,会触发组件重新渲染,实现视图刷新。
Q&A
Vue 响应式原理中的 Dep 和 Watcher 分别是什么?
Dep 代表依赖,是响应式数据对应的依赖对象;Watcher 是订阅者,可能来自 watch、computed 或渲染函数。Dep 与 Watcher 之间是多对多关系,互相记录以便在数据更新时通知。
Vue 如何知道哪些 Watcher 依赖于某个响应式数据?
Vue 在运行 Watcher 的回调函数前,会通过 Dep.target 记录当前 Watcher。当回调函数中使用响应式数据时,会触发 getter,在 getter 中通过 dep.depend() 将当前 Watcher 添加到 Dep 的订阅者列表中,从而建立依赖关系。
当响应式数据更新时,Vue 是如何触发视图更新的?
数据更新会触发 setter,setter 中调用 dep.notify(),遍历订阅者列表并调用每个 Watcher 的 update 方法。如果 Watcher 是渲染函数,则会触发组件更新,最终刷新视图。
Vue 响应式原理中的 Observer 有什么作用?
Observer 负责递归地将数据对象或数组转换为响应式,通过 walk 方法遍历对象属性并调用 defineReactive 进行响应式处理,处理后的对象会带有 __ob__ 属性作为标记。
defineReactive 函数在 Vue 响应式系统中做了什么?
defineReactive 为对象的每个属性创建一个 Dep 实例,并使用 Object.defineProperty 重写属性的 getter 和 setter。getter 中收集依赖,setter 中派发更新,从而实现响应式。
Vue 响应式原理中 Dep.target 的作用是什么?
Dep.target 用于在依赖收集阶段指向当前正在执行的 Watcher。当响应式数据被读取时,getter 通过 Dep.target 知道是哪个 Watcher 在依赖该数据,从而建立 Dep 与 Watcher 的关联。