Vue对象数据收集依赖具体流程

Posted yourgrandfather

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue对象数据收集依赖具体流程相关的知识,希望对你有一定的参考价值。

每一个属性都有一个dep对象,然后这个属性是被观察者,在同一个组件里面,每一个属性都对应到一个统一的watcher,这个watcher就是观察者,如果属性发生了改变,那么这个watcher就会调用get方法去重新渲染虚拟dom替换老的dom .这就是观察者模式

详细解释:在渲染的时候,每一个元素都会有一个对应的dep,然后这个dep在添加的时候id++,这样每给一个属性添加一个dep就会id++,这样id就唯一了.

​ 然后再执行$mount的时候,生成完了ast语法树,new了一个Watcher,在new的时候,Watcher这个对象里面有一些属性,如deps[],depsId(集合),然后在new的时候调用了Watcher原型身上的get方法,这个方法,将当前的Watcher实例对象赋值给了全局的Dep类上面的一个叫target属性,然后在生成虚拟dom时调用了变量的get方法,这个方法例调用了dep对象身上的depend方法,depend方法调用Watcher身上的addDep方法,把自己对应的dep作参传了过去,传过来之后就开始做判断,判断传送过来的dep的id是否存在于Watcher实例对象的这个depsId集合中,如果存在那么就不管了,如果不存在那么就向Watcher实例对象的dep中添加当前对应dep对象.并向集合中添加当前dep对应的id,最后再次调用传送过来的dep实例对象身上的这个addSub方法,将当前对应的Watcher实例对象传送过去,在addSub方法中将这个Watcher实例对象添加到当前所对应的这个dep对象里面.

​ __总结:__在初始化数据的时候给每一个属性都创建一个dep对象,然后利用闭包机制强行将这个dep对象保存在局部中,在生成虚拟dom的过程中,调用了当前dep对象的方法,这个方法利用全局变量Watcher调用了Watcher身上的方法把当前的dep对象传递过去,比较在Watcher中depsId集合中的id和当前dep对应的id,如果存在那么就不管证明了这个属性已经在被Watcher监视,如果没有那么就表示没有监视到当前这个属性,就需要给观察者Watcher中添加这个id和dep,最后传递过来的dep,调用dep身上的方法,将Watcher传送给dep,再将Watcher添加到dep中.这样就实现了Vue的数据依赖收集

​ __注:__这里只说了一个组件的逻辑,dep可能会针对多个组件,所以dep对象当中的subs数组,可能长度不为1,长度可能是>1,但是思路都是一样的

​ __下面说一下自动更新视图的逻辑:__在更改值的时候会调用这个sttter方法,在调用setter的时候会调用对应属性的这个dep身上的notify方法,notify中会拿到其数组中对应的所有watcher实例,然后分别调用每一个watcher实例身上的update方法,这个方法会重新执行一遍上面的__详细解释__里面的逻辑

vue响应性原理理解

技术图片

核心实现类

  1. Observer : 它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新
  2. Dep : 用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 Dep 实例(里面 subs 是 Watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。
  3. Watcher : 观察者对象 , 实例分为渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种

Watcher 和 Dep 的关系

watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。

依赖收集

initState 时,对 computed 属性初始化时,触发 computed watcher 依赖收集
initState 时,对侦听属性初始化时,触发 user watcher 依赖收集
render()的过程,触发 render watcher 依赖收集
re-render 时,vm.render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。

派发更新

组件中对响应的数据进行了修改,触发 setter 的逻辑
调用 dep.notify()
遍历所有的 subs(Watcher 实例),调用每一个 watcher 的 update 方法。

原理

当创建 Vue 实例时,vue 会遍历 data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。
每个组件实例会有相应的 watcher 实例,会在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有 computed watcher,user watcher 实例),之后依赖项被改动时,setter 方法会通知依赖与此 data 的 watcher 实例重新计算(派发更新),从而使它关联的组件重新渲染。

总结

  1. data 通过observer 转换成了getter/setter的形式来追踪变化
  2. 当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖中
  3. 当数据变化时,会触发setter从而向Dep中的依赖(watcher)发送通知
  4. Watcher接受通知之后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某一个回调函数等
    专业术语:
  • 数据劫持/数据代理
  • 依赖收集
  • 发布订阅

一句话总结:

vue.js 采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调

以上是关于Vue对象数据收集依赖具体流程的主要内容,如果未能解决你的问题,请参考以下文章

vueeventbus收集的依赖会一次性全部执行吗

实现一个最小版本vue之dep

vue响应性原理理解

vue响应性原理理解

Vue 依赖收集原理分析

Vue源码之响应式原理(个人向)