vue数据入口initSate开始分析数据驱动更新原理

前言

读这篇文章前,最好是先读vue数据绑定源码,由于本篇是接这章写的,放在一篇文章里,篇幅太大,我只好分红两章了。vue

初始化vue实例

Vue.prototype._init = function (options) {
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm) // 初始化数据的入口
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')
    
    if (vm.$options.el) {
      vm.$mount(vm.$options.el) // 挂载组件
    }
}

// mountComponent是在挂载组件时调用的方法
export function mountComponent (vm, el ,hydrating) {
  callHook(vm, 'beforeMount')

  let updateComponent = () => {
      vm._update(vm._render(), hydrating)
  }

  new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
  
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  
  return vm
}

initState

上一篇文章,咱们已经了解了ObserverDepWatcher都是负责什么?如何互相协做?
接下来,咱们从数据的入口开始,了解vue是如何使用这几个类完成的数据驱动视图更新的?
初始化state,就是初始化这几个属性。node

clipboard.png

先讲下什么是可观察者对象呢?
具有两个条件:
一、取值的时候,能把要取值的watcher(观察者对象)加入它的dep(依赖,也可叫观察者管理器)管理的subs列表里(即观察者列表);
二、设置值的时候,有了变化,全部依赖于它的对象(即它的dep里收集到的观察者watcher)都获得通知。
watcher里面存储它都观察了谁(dep),dep里面存储了都谁观察了本身。segmentfault

initProps

clipboard.png

循环每一个props属性,对每一个属性调用defineReactive,把每一个属性加上getset装饰器,变为可观察者对象,若是属性值是对象也会递归转化。数组

initData

clipboard.png

observe就是循环把data中的全部项都转换成可观察者对象,若是子项是对象或数组就递归转化,确保data里的每一项及其后代都转化成了可观察者对象
初始化数据时,无论data里的数据渲染用没用到,都会转成可观察者对象。
对于propsdata,只有哪一个watcher用到了去读取时,才会把该watcher加到他的观察者列表中。ide

dataprops里都调用了proxy这个方法,他是作什么的呢?
proxy(vm,’_data’,key)
proxy就是把dataprops下的属性都代理到了vm实例下,vm._data.a等价于vm.a
原理就是Object.definePropertyvmkey属性设置getset方法,当访问get的时候,返回的是vm._data[key];当访问set的时候,设置的是vm._data[key]的值。oop

initComputed

clipboard.png

初始化计算属性,就是为每个计算属性定义一个Watcher观察者对象。这个对象是lazy的,不会当即就去执行计算(即get方法),等到用的时候才会去计算,这个时候就会去读取这个计算属性依赖的可观察属性的值来计算,读取的时候就会把这些依赖添加进这个计算watcher里,同时这些依赖的订阅者列表也会加入这个计算watcher。因此当依赖变化时,通知到他的全部订阅watcher。计算watcher接到依赖发生变化了,不会当即计算新值,而是标记dirtytrue,读取这个计算属性的时候,发现dirtytrue,就是说数据已经不是最新的了,须要从新计算,而后才去计算,不然直接取上一次计算的值valuespa

若是某个data经过计算属性间接的被用到了渲染里,那么这个data也会被加入到渲染watcher的依赖列表,它的订阅者列表也会保存渲染watcher
只有当模版里使用了该计算属性,这个计算属性依赖的可观察者才会被加入到渲染watcher的依赖列表。若是模版里没有直接或间接用到可观察者对象属性,那么当你set它的时候,也就不会触发更新操做。prototype

initWatch

clipboard.png

初始化watch,就是为每一个watch属性建立一个观察者对象,这个expOrFn解析取值表达式去取值,而后就会调用相关data/prop属性的get方法,get方法又会在他的观察者列表里加上该watcher,一旦这些依赖属性值变化就会通知该watcher执行update方法。即会执行他的回调方法cb,也就是watch属性的handler方法。代理

到这里,数据的初始化就完成了。code

mountComponent

clipboard.png

这个方法是在,全部的数据初始化完成后,执行挂载组件时调用,建立一个渲染watcher,每一个组件有且仅有一个渲染watcherupdateComponentwatchergetter属性,建立后,当即调用get方法,便是调用updateComponent,也就是调用render方法,render里使用了的数据就会读取相关的dataget方法,就会把data的依赖加进这个渲染watcher的依赖列表里,datasubs也会加入渲染watcher,如此,当设置data时拦截的set方法就会通知渲染watcher调用update方法。

我总结下:
一个数据变动后,通知他的Watcher去执行update。
不一样类型的Watcher职责不一样,vue里的Watcher能够分为3类:
渲染Watcher、计算Watcher、侦听器Watcher,注意这3中Watcher的getter属性分别是什么。

  • 渲染Watcher的update,负责从新渲染,执行render;getter是updateComponent。
  • 计算Watcher的update,负责标记dirty,告诉它数据不是最新的须要从新计算了;getter是计算属性的get方法。
  • 侦听器Watcher的update,负责执行回调方法,也就是watch的handler;getter是watch的取值表达式。
相关文章
相关标签/搜索