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() 函数的方式来创建一个新的响应式数据对象,如下:
通过 reactive 函数所创建的数据都是unwrapperd
,同时 reactive 所创建返回的对象将会使用 Proxy 来构造一个新的对象来避免依赖更新的问题,这也就意味着:
与 Vue.js 2.0 相同,Reactive 中的数据同样都是deep
深度转换的。
Ref
在 Vue.js 3.0 中,增加了一种新的数据结构类型 Ref,使用 Ref 所构造的数据都是wrapped
,这意味着你可以分配一个新的 .value
给 Ref ,同样新的数据也仍然是响应式的。
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