Vue响应式原理解析

Vue响应式原理解析

💡 原文日文,约7300字,阅读约需18分钟。
📝

内容提要

Vue响应式原理:Dep负责数据依赖管理,Watcher为订阅者(包括watch、computed和渲染函数),Observer递归处理数据。核心流程:Watcher回调前设置Dep.target,读取数据触发getter建立依赖;数据更新触发setter,dep.notify通知Watcher更新视图,实现自动刷新。

🔎

延伸解读

Dep 与 Watcher 的多对多关系

Dep 的 subs 数组存储订阅该数据的 Watcher,Watcher 的 deps 数组存储其订阅的 Dep,形成多对多关系。这种设计允许一个数据被多个 Watcher 依赖,一个 Watcher 也可依赖多个数据。当数据更新时,Dep 通过 notify 通知所有订阅者;当 Watcher 不再需要时,可及时清理,避免内存泄漏。理解这一关系是掌握 Vue 响应式系统的关键。

Dep.target 的巧妙作用

Vue 通过全局的 Dep.target 记录当前正在执行的 Watcher。在 Watcher 回调执行前,会先将自身设为 Dep.target;回调中读取响应式数据时,触发 getter,getter 检查 Dep.target 存在则调用 dep.depend() 建立依赖。执行完毕后恢复 Dep.target。这种机制使得依赖收集自动化,无需手动声明,是响应式系统的核心技巧。

更新队列与异步更新

当数据变化触发 setter 时,Dep 会通知所有 Watcher 调用 update 方法。若 Watcher 未设置 sync,则不会立即执行更新,而是通过 queueWatcher 将 Watcher 推入队列,在下一个事件循环中批量执行。这避免了同一数据多次修改导致的重复渲染,提升了性能。理解异步更新机制有助于优化 Vue 应用,例如使用 nextTick 等待 DOM 更新。

computed 的惰性求值

computed 属性对应的 Watcher 具有 lazy 标志。当依赖数据变化时,update 方法仅将 dirty 标记为 true,并不立即重新计算。只有当其他代码访问该 computed 属性时,才会触发重新求值。这种惰性求值避免了不必要的计算,尤其适合复杂逻辑或多次访问的场景,是 Vue 性能优化的重要手段之一。

❓

Q&A

Vue响应式原理中Dep和Watcher分别是什么?

Dep是依赖管理器,每个响应式数据都会绑定一个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方法处理对象属性,通过observeArray处理数组元素,确保每个属性都被defineReactive处理,从而能够被Dep和Watcher追踪。

Vue的Watcher有哪些类型?它们分别对应什么?

Watcher有三种类型:watch(用户自定义的监听器)、computed(计算属性)和渲染函数(用于更新视图)。它们都是Dep的订阅者,当依赖变化时,会触发相应的回调。

Vue响应式原理中defineReactive函数做了什么?

defineReactive函数使用Object.defineProperty为对象的每个属性定义getter和setter。getter中收集依赖(通过dep.depend()),setter中触发更新(通过dep.notify())。同时,它还会递归处理嵌套对象,确保所有层级都是响应式的。

🏷️

标签

➡️

继续阅读