vue数据变化侦测

关于vue的内部原理其实有很多个重要的部分,变化侦测,模板编译,virtualDOM,整体运行流程等

今天主要分析下变化侦测的原理

如何侦测变化

关于变化侦测首先要问一个问题,在 js 中,如何侦测一个对象的变化

学过js的都能知道,js中有两种方法可以侦测到变化,Object.defineProperty 和 ES6 的proxy

到目前为止vue还是用的 Object.defineProperty

在vue的官网中,有这样一段关于vue追踪变化的原理

把一个普通对象传给Vue实例作为它的data选项,Vue.js将遍历它的属性,用Object.defineProperty将它们转换为getter/setter
用户看不到getter/setters,但是在内部它们让Vue.js追踪依赖,在属性被访问和修改时通知变化
模板中每个指令/数据绑定都有一个对应的watcher对象,在计算过程中它把属性记录为依赖;之后当依 赖的setter被调用时,会触发watcher重新计算,也就会导致它的关联指令更新DOM

接下来是小段源码

image

可以看到defineproperty中给属性getter,setter的设置,一旦该属性被获取,便会添加依赖;同样的,一旦该属性被更改,便会发出通知

观察所有数据

知识点补充:

访问器属性不能直接定义,必须使用Object.defineProperty()来定义;包含一对getter函数和setter函数(这两个函数不是必须的)

在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据

拿 Object.defineProperty来举例说明这个原理

image

所以,当对象下的访问器属性值发送改变之后(vue会将属性都转化为访问器属性),那么就会调用set函数,这时vue就可以通过这个set函数来追踪变化,调用相关函数来实现view视图的更新

每个组件实例都有相应的watcher实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而让致使它关联的组件得以更新

怎么观察

即在渲染的过程中就会调用对象属性的getter函数,然后getter函数通知watcher对象将之声明为依赖,依赖之后,如果对象属性发生变化,那么就会调用setter函数来通知watcher,watcher就会在重新渲染组件,以此来完成更新

总结起来一句话,getter中,收集依赖,setter中,触发依赖

依赖收集

首先想到的是每个key都有一个数组,用来存储当前key的依赖

实例中新增了对象数组model,用来存储被收集的依赖

然后在触发set触发时,循环model把收集到的依赖触发,递归侦测所有key

收集目标

收集谁,当数据变化通知谁?

watcher--变化侦测和更新view的一个纽带

watcher会主动将自己push到属性的依赖中,只有通过watcher读取属性的值才会收集依赖,模板是通过watcher读取属性的值的

总结

image

这是vue官网上的一张图,这张图其实非常清晰,就是一个变化侦测的原理图

getter到watcher有一条线,上面写着收集依赖,意思是说 getter 里收集 watcher,也就是说当数据发生 get 动作时开始收集 watcher

setter 到 watcher 有一条线,写着 Notify 意思是说在 setter 中触发消息,也就是当数据发生 set动作时,通知 watcher

Watcher 到 ComponentRenderFunction 有一条线,写着 Trigger re-render 意思很明显了

原生JS实现数据双向绑定