Open lulujianglab opened 6 years ago
关于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
接下来是小段源码
可以看到defineproperty中给属性getter,setter的设置,一旦该属性被获取,便会添加依赖;同样的,一旦该属性被更改,便会发出通知
知识点补充:
访问器属性不能直接定义,必须使用Object.defineProperty()来定义;包含一对getter函数和setter函数(这两个函数不是必须的)
在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据
拿 Object.defineProperty来举例说明这个原理
所以,当对象下的访问器属性值发送改变之后(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读取属性的值的
这是vue官网上的一张图,这张图其实非常清晰,就是一个变化侦测的原理图
getter到watcher有一条线,上面写着收集依赖,意思是说 getter 里收集 watcher,也就是说当数据发生 get 动作时开始收集 watcher
setter 到 watcher 有一条线,写着 Notify 意思是说在 setter 中触发消息,也就是当数据发生 set动作时,通知 watcher
Watcher 到 ComponentRenderFunction 有一条线,写着 Trigger re-render 意思很明显了
原生JS实现数据双向绑定
关于vue的内部原理其实有很多个重要的部分,变化侦测,模板编译,virtualDOM,整体运行流程等
今天主要分析下变化侦测的原理
如何侦测变化
关于变化侦测首先要问一个问题,在 js 中,如何侦测一个对象的变化
学过js的都能知道,js中有两种方法可以侦测到变化,Object.defineProperty 和 ES6 的proxy
到目前为止vue还是用的 Object.defineProperty
在vue的官网中,有这样一段关于vue追踪变化的原理
接下来是小段源码
可以看到defineproperty中给属性getter,setter的设置,一旦该属性被获取,便会添加依赖;同样的,一旦该属性被更改,便会发出通知
观察所有数据
知识点补充:
访问器属性不能直接定义,必须使用Object.defineProperty()来定义;包含一对getter函数和setter函数(这两个函数不是必须的)
在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据
拿 Object.defineProperty来举例说明这个原理
所以,当对象下的访问器属性值发送改变之后(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读取属性的值的
总结
这是vue官网上的一张图,这张图其实非常清晰,就是一个变化侦测的原理图
getter到watcher有一条线,上面写着收集依赖,意思是说 getter 里收集 watcher,也就是说当数据发生 get 动作时开始收集 watcher
setter 到 watcher 有一条线,写着 Notify 意思是说在 setter 中触发消息,也就是当数据发生 set动作时,通知 watcher
Watcher 到 ComponentRenderFunction 有一条线,写着 Trigger re-render 意思很明显了
原生JS实现数据双向绑定