从零实现一个响应式状态管理
概念
简单地说,响应式状态(state reactivity)是指当程序状态发生改变时,比如说某个变量的值发生了变化,就自动执行某些指定的操作。
这个功能主要分成两个部分:
当一个变量发生改变时,它能发出通知。
收集变量的依赖函数,即哪些函数对这个变量的变化是感兴趣的,把它们收集起来,变量改变时通知它们。
Dep Class
getter/setter
由于不存在变量“赋值钩子”这种东西,我们没法直接监听变量的修改情况,所以需要先把变量变成对象,用 getter
和 setter
来代替读取和赋值操作。
Dep
的实例是一个响应式的对象,目前它只提供了两个“钩子”(getter 和 setter),让我们可以在读值和赋值的时候进行某些操作。
notify
接下来我们来实现第一部分的功能,当变量值改变时,发出通知。
subscribers
里面存放着当前变量的依赖函数。notify
,通知各个依赖函数,在 setter 钩子里触发notify
,执行各个依赖函数。
depend
然后是收集依赖函数的工作。很简单,在 value 的 getter “钩子”里收集就行。
唯一的问题是,depend
中的依赖函数 func
是从哪里传进来的,this.depend()
是在 get value
中调用的,但 getter 也不能传参数呀。
这一点比较 tricky。在 get value
执行之前,执行流是在某个依赖函数 funcA
的内部,这时我们用一个全局变量把 funcA
存起来,接着在执行 depend
的时候读取那个全局变量就能得到 funcA
了。
activeUpdate
,存放当前依赖函数的全局变量。update
,变量count
的一个依赖函数
但是我们不能在每个依赖函数中都写上这句 activeUpdate = update;
吧。
上面代码需要重构一下,用一个 autorun
函数来完成依赖函数收集的功能,这样就不需要修改依赖函数本身的逻辑了。
到这里就差不多了,我们已经实现了对一个原始值变量的监听,不过我们在用 Vue 的时候,监听的是 data
对象的所有属性。
对于对象属性,JS 有提供一个 Object.defineProperty
API,可以直接将这个属性改成 getter 和 setter。
observe
函数把一个对象的所有属性都变成响应式的。Dep
就剩下depend
和notify
两个功能了。
完整代码
PS. 如果有人对完整代码中的 autorun 函数有疑问的话,可以看下这里,注意,个人理解而已。
Last updated