Reactivity响应式数据构建之路
Reactive模块的作用
本章我们将讲述在 Vue.js 中 Reactive 响应式数据是如何去构建的,与 Vue.js 2.0 相比 Vue.js 3.0 在底层仍然使用通过 Dep 进行数据收集依赖更新的方式来完成响应式数据处理。 但与之不同的是 Vue.js 3.0 新增了 Ref 数据结构,并使用了 ReactiveEffect 来替换以往使用 Watcher 来监听并更新数据的方式,同时增加了新的 effectScope 作用域来提升性能。接下来让我们详细介绍 Vue.js 3.0 所带来的新的响应式数据处理变化。
Reactive与Ref
Reactive
与 Vue.js 2.0 返回一个创建 Data 响应式数据的函数相比,Vue.js 3.0 则是采用了使用 reactive() 函数的方式来创建一个新的响应式数据对象,如下:
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>通过 reactive 函数所创建的数据都是unwrapperd,同时 reactive 所创建返回的对象将会使用 Proxy 来构造一个新的对象来避免依赖更新的问题,这也就意味着:
const obj = {}
reactive(obj) != obj // 返回新的Proxy对象与 Vue.js 2.0 相同,Reactive 中的数据同样都是deep深度转换的。
Ref
在 Vue.js 3.0 中,增加了一种新的数据结构类型 Ref,使用 Ref 所构造的数据都是wrapped,这意味着你可以分配一个新的 .value给 Ref ,同样新的数据也仍然是响应式的。
interface Ref<T> {
value: T
}
function ref<T>(value: T): Ref<UnwrapRef<T>>ShallowReactive与ShallowRef
除此以外,Vue 还新增加了两种变体类型 ShallowReactive 与 ShallowRef :
顾名思义,ShallowReactive 、 ShallowRef 与前者不同的区别在于其不是deep转换的,因此:
与前者相比 ShallowReactive 与 ShallowRef 对性能更好友好,更加适合一些不常更新的数据对象,以避免内存资源的过渡占用。
响应式数据是如何实现的?
前面我们介绍了在 Vue.js 3.0 中的新的API与其功能,现在是时候来谈下其底层原理, Vue 是如何来实现数据响应式处理的,首先我们将介绍一种非常重要的基础数据结构 Dep 。
Dep与数据收集
在 Vue.js 2.0与3.0中 Dep 都是实现整个响应式数据的核心,Dep 是一种如下的数据结构:
它很简单仅仅拥有两个属性w: wasTracked与n: newTracked,但却是整个响应式数据实现的根本。
首先我们需要知道对于一个响应式数据更新而言,他需要有两个最基本的角色:观察器Effect与收集器Dep,每当有数据需要被更新时Dep负责收集更新信息,告知Effect,哪些数据需要被更新,而Effect则负责处理这些信息并最终完成视图的更新。
那么我们首先来看Dep是如何被创建并完成收集的:
在完成了Dep的创建与收集之后,我们完成了响应式数据处理的第一步,即:收集数据,那么现在我们到了第二步:如何去监听数据的变化。
Effect与视图更新
由于Dep的创建与收集是在 Proxy Getter 中完成的,那么数据的变化监听自然则是在 Proxy Setter 中完成。
与 Vue.js 2.0 使用 Watcher 来实现数据监听不同,在 Vue.js 3.0 中则是使用了 Effect来完成这个操作。首先我们来看下Effect是什么?
简单的来说Effect是一个收集器,它会收集Dep的数据更新信息,每当有属性需要被更新时,会触发 Proxy Setter 钩子,然后检查需要被更新的数据Dep通知与当前Dep数据所绑定的Effect,Effect在接收到更新信息之后将会调用scheduler函数将此EffectScope范围内的视图加入到下一个更新队列中,从而完成响应式数据的视图更新。
EffectScope
前面我们讲述了,Dep与Effect是如何分别在 Proxy 的Getter与Setter中,完成相互的绑定与视图更新的,同时我们也提到了在 Vue.js 3.0 中的一个新的属性 EffectScope,那么 EffectScope 有什么用呢?
在 Vue.js 2.0 中 Vue 对于任意的响应式数据(data)、计算属(computed)性、观察器(watcher)以及component组件都会构建一个Watcher观察器,对于数据、计算属性等的观察器而言,是用于监听数据的变化更新,而对于component组件而言这个观察器则是用于组件的视图更新。
我们提到了在 Vue.js 3.0 中 Vue 使用了Effect来替换了以往的Watcher,而EffectScope则正是替换了以往的component组件的Watcher。不仅如此EffectScope现在提供了更为灵活的使用方式,以允许你构建一片区域,在这片区域中的任何data、computed、watcher等所构建的efffect都将得以独立运行。
计算属性与观察器
与Ref和Reactive函数所构造的响应式数据相同,Computed计算属性与Watch观察属性本质上都是构建了一个自身的Effect对象,只是他们在使用上的用途不同。
Computed计算属性
Computed计算属性:计算属性接收一个Getter函数然后返回一个仅可读的Ref对象。
Watch观察器
Watch观察器用于监听数据的变化,并执行相对应的回调函数,
与 Vue.js 2.0 相比 Vue.js 3.0 新增了flush属性与onTrack、onTrigger两个钩子函数,flush的三个属性 'pre' | 'post' | 'sync'分别对应Watch钩子函数在组件刷新前、中、后三个不同的时期运行,而onTrack与onTrigger则分别是数据收集与刷新时所要触发的钩子函数。
总结
得益于 Vue.js 3.0 在 Componsition Api 与 Setup 上所带来的代码语法书写上的提升,Vue 引入了全新的声明式函数 Ref 以及 Reactive 来满足新的语法需求。同时增加了 shallowRef 与 toRefs 之类的变体函数来丰富功能的多样式。
在使用了新的 ActiveEffect 来作为观察器替代了以往的 Watcher 来作为数据监听之后,Vue 新增了 EffectScope 作用域来更进一步提升使用与性能。
本章我们主要介绍了 Vue.js 3.0 的 Reactive 模块的功能以及是如何去实现数据的双向绑定的,下一章我们将详情介绍 Vue.js 应用是如何去运行,以及组件是如何去更新的。
Last updated