Vue 的响应式系统是实现应用状态变更时自动更新 UI 的基础。本文将深入探讨 Vue 响应式系统的工作原理,涵盖其核心概念、架构和实现细节。
有关 refs 和 computed properties 等特定响应式 API 的信息,请参阅 Refs and Reactive Objects 和 Computed and Watch。
Vue 的响应式系统建立在三个主要概念之上:
Vue 提供了多种 API 来创建响应式状态。
| API | 目的 | 行为 |
|---|---|---|
reactive() | 创建一个响应式对象。 | 深度响应式,支持自动解包 ref。 |
ref() | 使原始类型数据具有响应性。 | 将值包装在一个带有 .value 属性的响应式对象中。 |
readonly() | 创建只读状态。 | 防止对响应式对象进行修改。 |
shallowReactive() | 创建浅层响应性。 | 仅根级别的属性是响应式的。 |
shallowRef() | 创建浅层 ref。 | 对象值的深度转换无效。 |
来源:packages/reactivity/src/reactive.ts91-104 packages/reactivity/src/ref.ts55-61
以下是响应式系统组件如何交互的高层概述:
来源:packages/reactivity/src/dep.ts256-277 packages/reactivity/src/effect.ts476-497
当在副作用执行期间访问响应式属性时,Vue 会自动跟踪这些依赖关系。这种跟踪利用了带有自定义处理程序的 JavaScript Proxy 来拦截属性访问。
追踪是通过 track 函数实现的,该函数为特定属性创建或检索依赖项(Dep),并将当前副作用添加为订阅者。
来源:packages/reactivity/src/dep.ts102-146 packages/reactivity/src/baseHandlers.ts55-133
副作用系统决定了在响应式状态发生变化时需要重新计算的内容。其核心是 ReactiveEffect 类。
当响应式属性发生更改时,它会通过其依赖项触发通知,进而通知所有依赖于它的副作用。这形成了一个自动化的响应式链。
来源:packages/reactivity/src/effect.ts86-216 packages/reactivity/src/dep.ts67-199
Vue 使用 JavaScript 的 Proxy 功能创建响应式对象。当您调用 reactive() 时,Vue 会创建一个 Proxy,拦截属性的访问和修改。
Proxy 处理程序包含各种操作的陷阱:
get:追踪属性访问,并返回嵌套对象的响应式版本。set:在属性更改时触发更新。deleteProperty:在属性被删除时触发更新。has:追踪属性存在性检查。ownKeys:追踪迭代。来源:packages/reactivity/src/reactive.ts91-104 packages/reactivity/src/baseHandlers.ts136-185
Refs 将值包装在一个带有 .value 属性的响应式对象中。
当访问 .value 属性时,ref 会追踪依赖项。当设置 .value 时,如果值发生变化,它会触发更新。
来源:packages/reactivity/src/ref.ts108-159
计算属性是特殊的 ref,它们会自动跟踪其依赖项,并且仅在这些依赖项发生更改时才重新计算。
计算属性
来源:packages/reactivity/src/computed.ts47-154 packages/reactivity/src/computed.ts189-220
所有组件协同工作的方式如下:
来源:packages/reactivity/src/dep.ts288-383 packages/reactivity/src/effect.ts150-180
由于 JavaScript 集合(Map、Set、WeakMap、WeakSet)的独特方法,Vue 的响应式系统需要特别处理它们。
对于集合,Vue 提供了自定义处理程序来拦截 get、set、add、delete 等方法以及迭代方法。
来源:packages/reactivity/src/collectionHandlers.ts169-205
Vue 提供了 shallowReactive() 和 shallowRef(),适用于不需要或不希望进行深度响应性的场景。这些变体仅在根级别跟踪响应性。
来源:packages/reactivity/src/reactive.ts140-150 packages/reactivity/src/ref.ts86-96
Vue 允许通过 readonly() 和 shallowReadonly() 创建响应式对象的只读版本。这些版本可以防止修改,同时保留读取时的响应性。
来源:packages/reactivity/src/reactive.ts205-215 packages/reactivity/src/baseHandlers.ts218-242
有时,您可能希望选择不为某些对象启用响应性。
| API | 目的 |
|---|---|
markRaw() | 永久阻止对象被响应式化。 |
toRaw() | 从 Proxy 对象获取原始的、未响应式化的对象。 |
来源:packages/reactivity/src/reactive.ts378-381 packages/reactivity/src/reactive.ts407-412
Vue 的响应式系统将基于 Proxy 的观察机制与高效的依赖追踪系统相结合。当在副作用执行期间访问属性时,它们会被自动追踪。当修改属性时,它们会触发响应式链,最终更新 UI。
该系统功能强大且灵活,提供了微调响应性行为的选项:
reactive() 和 ref() 进行基本响应性。readonly() 进行只读状态管理。shallowReactive() 和 shallowRef() 进行浅层追踪。markRaw() 和 toRaw() 进行性能优化。这种架构使 Vue 具备了标志性的声明式和响应式编程模型,同时保持了卓越的性能。