带你彻底掌握 Vue 3.0 的响应式系统

熟悉vue的小伙伴应该都知道 , 谈到vue的原理 , 最重要的莫过于:响应式 , 虚拟dom及diff算法.
随着 Vue 3.0 Pre Alpha 版本的公布 , 我们得以一窥其源码的实现 。Vue 最巧妙的特性之一是其响应式系统 , 而我们也能够在仓库的 packages/reactivity 模块下找到对应的实现 。虽然源码的代码量不多 , 网上的分析文章也有一堆 , 但是要想清晰地理解响应式原理的具体实现过程 , 还是挺费脑筋的事情 。经过一天的研究和整理 , 我把其响应式系统的原理总结成了一张图 , 而本文也将围绕这张图去讲述具体的实现过程 。
 

带你彻底掌握 Vue 3.0 的响应式系统

文章插图
 
 
一个基本的例子【带你彻底掌握 Vue 3.0 的响应式系统】Vue 3.0 的响应式系统是独立的模块 , 可以完全脱离 Vue 而使用 , 所以我们在 clone 了源码下来以后 , 可以直接在 packages/reactivity 模块下调试 。
  1. 在项目根目录运行 yarn dev reactivity , 然后进入 packages/reactivity 目录找到产出的 dist/reactivity.global.js 文件 。
  2. 新建一个 index.html , 写入如下代码:
<script src=https://www.isolves.com/it/cxkf/bk/2020-04-13/"./dist/reactivity.global.js">3.在浏览器打开该文件 , 于控制台执行 state.count++ , 便可看到输出 set count to 1 。
在上述的例子中 , 我们使用 reactive() 函数把 origin 对象转化成了 Proxy 对象 state;使用 effect() 函数把 fn() 作为响应式回调 。当 state.count 发生变化时 , 便触发了 fn() 。接下来我们将以这个例子结合上文的流程图 , 来讲解这套响应式系统是怎么运行的 。
 初始化阶段 
带你彻底掌握 Vue 3.0 的响应式系统

文章插图
 
 
在初始化阶段 , 主要做了两件事 。
  1. 把 origin 对象转化成响应式的 Proxy 对象 state 。
  2. 把函数 fn() 作为一个响应式的 effect 函数 。
首先我们来分析第一件事 。
大家都知道 , Vue 3.0 使用了 Proxy 来代替之前的 Object.defineProperty() , 改写了对象的 getter/setter , 完成依赖收集和响应触发 。但是在这一阶段中 , 我们暂时先不管它是如何改写对象的 getter/setter 的 , 这个在后续的”依赖收集阶段“会详细说明 。为了简单起见 , 我们可以把这部分的内容浓缩成一个只有两行代码的 reactive() 函数:
export function reactive(target) {const observed = new Proxy(target, handler)return observed}接下来我们分析第二件事 。
当一个普通的函数 fn() 被 effect() 包裹之后 , 就会变成一个响应式的 effect 函数 , 而 fn() 也会被立即执行一次 。
由于在 fn() 里面有引用到 Proxy 对象的属性 , 所以这一步会触发对象的 getter , 从而启动依赖收集 。
除此之外 , 这个 effect 函数也会被压入一个名为”activeReactiveEffectStack“(此处为 effectStack)的栈中 , 供后续依赖收集的时候使用 。
来看看代码(完成代码请看 effect.js):
export function effect (fn) {// 构造一个 effectconst effect = function effect(...args) {return run(effect, fn, args)}// 立即执行一次effect()return effect}export function run(effect, fn, args) {if (effectStack.indexOf(effect) === -1) {try {// 往池子里放入当前 effecteffectStack.push(effect)// 立即执行一遍 fn()// fn() 执行过程会完成依赖收集 , 会用到 effectreturn fn(...args)} finally {// 完成依赖收集后从池子中扔掉这个 effecteffectStack.pop()}}}至此 , 初始化阶段已经完成 。接下来就是整个系统最关键的一步——依赖收集阶段 。
 
依赖收集阶段 
带你彻底掌握 Vue 3.0 的响应式系统

文章插图
 
 
这个阶段的触发时机 , 就是在 effect 被立即执行 , 其内部的 fn() 触发了 Proxy 对象的 getter 的时候 。简单来说 , 只要执行到类似 state.count 的语句 , 就会触发 state 的 getter 。


推荐阅读