手动打造一个MVVM
框架
一、核心原理简述
利用Object.defineProperty()
来劫持数据,当对某个属性进行update
操作的时候,会触发它的一组Subscritber
,并调用他们的update
方法,这种方式就是典型的观察者模式。
1、Observer
观察者
在vue
源码里,它的角色就是观察者,通过Object.defineProperty
来代理数据(现在是Proxy
)。在数据更新的时候通知对应的订阅者,订阅者在收到提示之后做自己的业务逻辑,要么更新视图,要么更新其他数据,或者进行业务操作。
这里比较厉害的是依赖收集,如何确定数据和订阅者之间的关系?按照最朴素的想法就是,我订阅者作为主动订阅的一方,那么肯定是主动向观察者发出申请,表明我对某个属性的变化感兴趣。观察者在收到申请之后将数据和订阅者关联起来,在数据变化时通知订阅者。很完美不是吗?这能够满足最基本的数据双向绑定,但是不要忘了,在Vue
中数据和订阅者不是简单的 1:n
的关系,而是n:n
的关系。 例如某个Computed
计算属性A
依赖data1
、data2
,在他们任意一个发生变化的时候重新计算,给出计算后的结果。这是如何做到的?又或者是说Vue
又是怎么知道计算属性A
依赖data1
和data2
的?通过语法分析解析你的表达式?这种方法可不可行我不知道,但Vue
有更聪明的办法,那就是依赖收集。
如上图所示,在订阅者调用Data1
的时候,将全局变量-当前调用者(在Vue
中是Dep.target
)指向自己,然后去get
数据,由于Data1
被Object.defineProperty()
代理过,所以在其属性访问器get()
内可以知道是谁在调用自己,并将其视为一个对自己的依赖,把它收集到自己的Registry
中(在Vue
中叫做Dep
,意为依赖),这里说一下Registry
,一个数据可以被多个订阅者订阅,一个订阅者也可以订阅多个数据。那么每个数据维护一个自己的订阅者列表就可以表达这种关系,这个订阅者列表就是Registry
。然后,在数据被更新的时候,通知订阅者列表中的每个订阅者。这样一个闭环就完成了!
这种方式从我们一开始想的主动订阅变成了被动收集,换一个角度就解决了如何确定依赖关系这种棘手的问题。