Reactivity响应式数据构建之路

Reactive模块的作用

本章我们将讲述在 Vue.jsReactive 响应式数据是如何去构建的,与 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,这意味着你可以分配一个新的 .valueRef ,同样新的数据也仍然是响应式的。

interface Ref<T> {
  value: T
}

function ref<T>(value: T): Ref<UnwrapRef<T>>

ShallowReactive与ShallowRef

除此以外,Vue 还新增加了两种变体类型 ShallowReactiveShallowRef

顾名思义,ShallowReactiveShallowRef 与前者不同的区别在于其不是deep转换的,因此:

与前者相比 ShallowReactiveShallowRef 对性能更好友好,更加适合一些不常更新的数据对象,以避免内存资源的过渡占用。

响应式数据是如何实现的?

前面我们介绍了在 Vue.js 3.0 中的新的API与其功能,现在是时候来谈下其底层原理, Vue 是如何来实现数据响应式处理的,首先我们将介绍一种非常重要的基础数据结构 Dep

Dep与数据收集

Vue.js 2.0与3.0中 Dep 都是实现整个响应式数据的核心,Dep 是一种如下的数据结构:

它很简单仅仅拥有两个属性w: wasTrackedn: 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数据所绑定的EffectEffect在接收到更新信息之后将会调用scheduler函数将此EffectScope范围内的视图加入到下一个更新队列中,从而完成响应式数据的视图更新。

EffectScope

前面我们讲述了,DepEffect是如何分别在 ProxyGetterSetter中,完成相互的绑定与视图更新的,同时我们也提到了在 Vue.js 3.0 中的一个新的属性 EffectScope,那么 EffectScope 有什么用呢? 在 Vue.js 2.0Vue 对于任意的响应式数据(data)、计算属(computed)性、观察器(watcher)以及component组件都会构建一个Watcher观察器,对于数据、计算属性等的观察器而言,是用于监听数据的变化更新,而对于component组件而言这个观察器则是用于组件的视图更新。 我们提到了在 Vue.js 3.0Vue 使用了Effect来替换了以往的Watcher,而EffectScope则正是替换了以往的component组件的Watcher。不仅如此EffectScope现在提供了更为灵活的使用方式,以允许你构建一片区域,在这片区域中的任何datacomputedwatcher等所构建的efffect都将得以独立运行。

计算属性与观察器

RefReactive函数所构造的响应式数据相同,Computed计算属性与Watch观察属性本质上都是构建了一个自身的Effect对象,只是他们在使用上的用途不同。

Computed计算属性

Computed计算属性:计算属性接收一个Getter函数然后返回一个仅可读的Ref对象。

Watch观察器

Watch观察器用于监听数据的变化,并执行相对应的回调函数,

Vue.js 2.0 相比 Vue.js 3.0 新增了flush属性与onTrackonTrigger两个钩子函数,flush的三个属性 'pre' | 'post' | 'sync'分别对应Watch钩子函数在组件刷新前、中、后三个不同的时期运行,而onTrackonTrigger则分别是数据收集与刷新时所要触发的钩子函数。

总结

得益于 Vue.js 3.0Componsition ApiSetup 上所带来的代码语法书写上的提升,Vue 引入了全新的声明式函数 Ref 以及 Reactive 来满足新的语法需求。同时增加了 shallowReftoRefs 之类的变体函数来丰富功能的多样式。

在使用了新的 ActiveEffect 来作为观察器替代了以往的 Watcher 来作为数据监听之后,Vue 新增了 EffectScope 作用域来更进一步提升使用与性能。

本章我们主要介绍了 Vue.js 3.0Reactive 模块的功能以及是如何去实现数据的双向绑定的,下一章我们将详情介绍 Vue.js 应用是如何去运行,以及组件是如何去更新的。

Last updated