菜单

响应式系统

相关源文件

Vue 的响应式系统是实现应用状态变更时自动更新 UI 的基础。本文将深入探讨 Vue 响应式系统的工作原理,涵盖其核心概念、架构和实现细节。

有关 refs 和 computed properties 等特定响应式 API 的信息,请参阅 Refs and Reactive ObjectsComputed and Watch

核心概念

Vue 的响应式系统建立在三个主要概念之上:

  1. 响应式状态:通过 JavaScript Proxy 实现的响应式对象。
  2. 副作用追踪:在渲染或计算过程中自动进行依赖追踪。
  3. 变更检测:拦截突变并触发更新。

响应式状态创建

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 类。

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

Refs 将值包装在一个带有 .value 属性的响应式对象中。

当访问 .value 属性时,ref 会追踪依赖项。当设置 .value 时,如果值发生变化,它会触发更新。

来源:packages/reactivity/src/ref.ts108-159

计算属性

计算属性是特殊的 ref,它们会自动跟踪其依赖项,并且仅在这些依赖项发生更改时才重新计算。

计算属性

  1. 管理自身的依赖追踪。
  2. 惰性求值(仅在访问时)。
  3. 缓存结果,直到依赖项发生更改。
  4. 正确处理链式计算。

来源:packages/reactivity/src/computed.ts47-154 packages/reactivity/src/computed.ts189-220

响应式系统工作流程

所有组件协同工作的方式如下:

  1. 当 Vue 创建组件时,它会将渲染函数包装在一个响应式副作用中。
  2. 在渲染过程中,任何被访问的响应式属性都会被追踪为依赖项。
  3. 当响应式属性发生更改时,它会通知其依赖项。
  4. 依赖项通知它们的订阅者(副作用)。
  5. 副作用重新执行,更新 DOM。

来源:packages/reactivity/src/dep.ts288-383 packages/reactivity/src/effect.ts150-180

集合处理

由于 JavaScript 集合(Map、Set、WeakMap、WeakSet)的独特方法,Vue 的响应式系统需要特别处理它们。

对于集合,Vue 提供了自定义处理程序来拦截 getsetadddelete 等方法以及迭代方法。

来源: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 具备了标志性的声明式和响应式编程模型,同时保持了卓越的性能。