【Vue.js设计与实现】第四章:响应式系统作用与实现(二)

209 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

在响应式系统作用与实现(一)中,我们初步实现了响应式系统,并且通过提供一个用来注册副作用函数的机制来摆脱了响应式系统对副作用函数名称的依赖。 但是此时的响应式系统还是有缺陷的,因为我们在编写响应式系统的时候一直都是在围绕着对象的一个属性来进行设计的,那么加入我们此时给对象obj新增一个属性,然后对这个属性进行读写,会不会触发副作用函数呢? 答案是会的。

当我们为obj对象新增一个不存在的属性的时候,匿名副作用函数被触发了,这就说明我们的响应式系统中 没有在副作用函数与被操作的目标字符按之间建立明确的关系。  因此,我们需要重新设计“桶”的数据结构。

偶然看到一句话,觉得说的很对:
响应式系统的核心其实就是一个数据结构。

原先的Set数据结构,导致我们在读取属性的时候,无论读取的是哪一个属性,都会把副作用函数收集到“桶”里;当我们设置属性时,无论设置的是哪一个属性,都会从“桶”里取出副作用函数并执行。 副作用函数和被操作的字段(属性)之间没有明确的关系。 没有明确的关系,那我们就想想该如何建立明确的关系呢?

我们仔细想想,整个响应式系统中有下面这3种角色:

  • 被操作(读取)的代理对象 obj;
  • 被操作(读取)的字符按名称 text;
  • 使用effect 函数注册的副作用函数 effectFn;

我们将3个角色抽象一下,使用 target 来表示一个代理对象所代理的 原始对象, 使用 key 来表示被操作的字段名称, 用 effectFn 来表示被注册的副作用函数,则3者关系如下:

target
   └─ key
       └─ effectFn

这是一种 树形结构

然后我们可以对这个树形结构可能出现的情况做个归纳:

  1. 两个副作用函数eFn1和eFn2同时读取同一个对象的text属性的属性值;
target
   └─ text
       └─ eFn1
       └─ eFn2
  1. 一个副作用函数eFn读取同一个对象的两个不同属性text1和text2的值;
target
   └─ text1
       └─ eFn
   └─ text2
       └─ eFn
  1. 不同副作用函数中,读取两个不同对象target1和target2的不同属性;
target1
   └─ text1
       └─ effectFn1
target2
   └─ text2
       └─ effectFn2

通过这种树形结构,我们就将副作用函数和被操作的字段之间的联系建立了起来,这样当我们设置obj.text2的值的时候,就 只会 导致其所关联的副作用函数重新执行,并不会影响其他的副作用函数的执行。

然后,我们可以通过下面的代码来实现新的 “桶”

image.png