深入浅出Vue.js一之Object的变化侦测
写在前面
深入浅出系列是基于《深入浅出Vue.js》这本书的一系列个人理解,如有错误,恳请指正。
问:什么是渐进式框架?
在这里先引用一句vue的定位理念:The Progressive Framework.所谓渐进式框架,就是把框架分层。在vue里面,我们可以把它想象成一层一层嵌套的图,由内而外的是视图渲染层,组件机制,路由机制,状态管理,构建工具。所谓分层,就是你可以既可以只是用核心的视图层渲染功能进行快速开发,也可以利用一整套全家桶来开发大型应用。所以渐进式就是一层层分工明确但是可以组合完成不同需求的一个框架。
变化侦测
侦测变化,数据改变,驱动视图改变。记得一点,数据驱动视图。
什么是变化侦测?
变化侦测:变化侦测就是在我们的应用内部的状态不断发生变化的时候,确定这些变化发生了什么的一种侦测机制。就好像是一个监控,去看哪些地方的事物发生了变化,进而去通知其他地方。
变化侦测分两种类型:一种是拉,一种是推。Vue属于推,就是发生变化的时候,Vue是知道哪里发生了变化,然后对发生变化的位置进行处理。另外两个框架则是相反,是知道变化,然后去寻找发生变化的地方。在这里,推的粒度更细,因为在一开始就会存在很多依赖去实现这种发生变化就知道的效果,所有会消耗内存更多一些。所以两种方式各有优缺点,同时也印证了一句话,没有绝对的事情。
如何追踪变化?
如何追踪,其实说的就是如何侦测。从代码上来说要实现侦测,利用JS的Object.defineProperty和ES6的Proxy。那么如何实现呢,上代码:
由于在下也是第一次用这个Object.defineProperty,个人理解如下:我们这个函数其实是封装了Object.defineProperty的方法,首先这个方法接受三个参数:对象,key,val,我们穿进去之后,改对象下面的key就被处于妻管严的状态了。只要发生变化,就会触发set方法(get方法在获取值的时候触发,比如data.name),如果值相同,不改变。如果值不同,则为新的值。
在书中的原话是:每当从data的key中读取数据的时候,get函数被触发;每当往data的key中设置数据时,set函数被触发。
如何收集依赖?
只有数据监听是没有什么实际的用处,我们需要知道的哪些东西发生了变化,将其收集起来,进行下一步的处理。那么如何处理这些依赖呢,一句话:在getter中收集依赖,在setter中触发依赖。
依赖收集在哪里
目前来看每一个key都有一个数组,存着它的依赖。触发的节点通过window.target获取(本人写的时候实际是window.event.target)。这里直接上优化版的demo,写一个dep类存放他的依赖。
这里我们写了一个类用来实现对依赖的管理(原本是一个数组,不太合理),现在我们收集到了依赖window.target。那么依赖是什么呢?就是发生变化的时候,我们需要通知谁。就是接下来的watcher
什么是watcher?
watcher是一个中介,数据变化的时候告诉它,由它通知其他地方。现在是书中的一个watcher实例:
个人解读如下:在这里的get方法中,this指向了window.target,再读一次data.a.b.c触发自己的getter就会触发在之前getter里面的dep.depend方法将依赖收集,当值发生变化,触发notify方法,实际是调用Watcher里的update实现通知。这里需要补充call的用法。
递归侦测所有的key
其实这里所有被侦测的数据都是转换成了getter/setter的模式才得以实现,前面的方法只是使用与单一的数据。实际需要一个类递归实现全部的属性侦测。
关于Object的问题
在很久以前只是知道,在data里面创建的对象,在后续添加一个属性或者删除一个属性是无法使视图更新的。原因就是因为这个getter/setter,在添加属性或者删除属性的时候无法触发那两个方法,所以不会导致视图的更新。因为getter/setter只有在设置值和读取值的时候触发(不知道vue3里面的实现是如何,但是在2里面的情况就是这样。)