例子
Vue 3.0 的响应式系统是独立的模块,可以完全脱离 Vue 而使用。
控制台执行 state.count++,便可看到输出 set count to 1。
在上述的例子中,使用 reactive() 函数把 origin 对象转化成了 Proxy 对象 state;使用 effect() 函数把 fn() 作为响应式回调。当 state.count 发生变化时,便触发了 fn()。
接下来将以这个例子并结合流程图,讲解响应式系统运行的过程:
初始化阶段:
在初始化阶段,主要做了两件事。
- 把 origin 对象转化成响应式的 Proxy 对象 state。
- 把函数 fn() 作为一个响应式的 effect 函数。
第一件事:
Vue 3.0 使用了 Proxy 来代替之前的 Object.defineProperty(),改写了对象的 getter/setter,完成依赖收集和响应触发。为了简单起见,把这部分的内容浓缩成一个只有两行代码的 reactive() 函数:
第二件事:
当一个普通的函数 fn() 被 effect() 包裹之后,就会变成一个响应式的 effect 函数,而 fn() 也会被立即执行一次。
由于在 fn() 里面有引用到 Proxy 对象的属性,所以这一步会触发对象的 getter,从而启动依赖收集。
除此之外,这个 effect 函数也会被压入一个名为”activeReactiveEffectStack“(此处为 effectStack)的栈中,供后续依赖收集的时候使用。
看看代码:
至此,初始化阶段已经完成。
依赖收集阶段
这个阶段的触发时机,就是在 effect 被立即执行,其内部的 fn() 触发了 Proxy 对象的 getter 的时候。简单来说,只要执行到类似 state.count 的语句,就会触发 state 的 getter。
依赖收集阶段最重要的目的,就是建立一份”依赖收集表“,也就是图示的”targetMap"。targetMap 是一个 WeakMap,其 key 值是当前的 Proxy 对象 state,而 value 则是该对象所对应的 depsMap。
depsMap 是一个 Map,key 值为触发 getter 时的属性值(此处为 count),而 value 则是触发过该属性值所对应的各个 effect。
举个例子,假设有个 Proxy 对象和 effect 如下:
那么这里的 targetMap 应该为这个样子:
这样,{ target -> key -> dep } 的对应关系就建立起来了,依赖收集也就完成了。
响应阶段
在控制台上看看效果:
效果符合预期,那么它是怎么实现的呢?首先来看看这个阶段的原理图:
当修改对象的某个属性值的时候,会触发对应的 setter。setter 里面的 trigger() 函数会从依赖收集表里找到当前属性对应的各个 dep,然后把它们推入到 effects 和 computedEffects(计算属性) 队列中,最后通过 scheduleRun() 挨个执行里面的 effect。
原博客:https://jrainlau.github.io/#/article?number=20&title=%E4%B8%80%E5%BC%A0%E5%9B%BE%E7%90%86%E6%B8%85%20Vue%203.0%20%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F%E7%B3%BB%E7%BB%9F
本文暂时没有评论,来添加一个吧(●'◡'●)