new Vue() => _init() => initState:javascript
function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
判断该vue实例是否存在props
、methods
、data
、computed
、watch
进行调用相应的初始化函数html
主要工做是调用defineProperty
给属性分别挂载get(触发该钩子时,会将当前属性的dep实例推入当前的Dep.target也就是当前watcher的deps中即它订阅的依赖,Dep.target下文会讲到。且该dep实例也会将当前watcher即观察者推入其subs数组中)、set方法(通知该依赖subs中全部的观察者watcher去调用他们的update方法)。vue
它的做用是将computed对象中全部的属性遍历,并给该属性new一个computed watcher(计算属性中定义了个dep依赖,给须要使用该计算属性的watcher订阅)。也会经过调用defineProperty
给computed挂载get(get方法)、set方法(set方法会判断是否传入,若是没传入会设置成noop空函数)computed
属性的get方法是下面函数的返回值函数java
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
注意其中的watcher.depend()
,该方法让用到该属性的watcher观察者订阅该watcher中的依赖,且该计算属性watcher会将订阅它的watcher推入他的subs中(当计算属性值改变的时候,通知订阅他的watcher观察者)watcher.evaluate()
,该方法是经过调用watcher的get方法(其中须要注意的是watcher的get方法会调用pushTarget将以前的Dep.target实例入栈,并设置Dep.target为该computed watcher,被该计算属性依赖的响应式属性会将该computed watcher推入其subs中,因此当被依赖的响应式属性改变时,会通知订阅他的computed watcher,computed watcher 再通知订阅该计算属性的watcher调用update方法),get方法中调用计算属性key绑定的handler函数计算出值。数组
该watcher 为user watcher(开发人员本身在组件中自定义的)。
initWatch的做用是遍历watch中的属性,并对每一个watch监听的属性调用定义的$watchapp
Vue.prototype.$watch = function ( expOrFn: string | Function, cb: any, options?: Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {} options.user = true // 表明该watcher是用户自定义watcher const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { cb.call(vm, watcher.value) } return function unwatchFn () { watcher.teardown() } }
代码中调用new Watcher的时候,也会同render watcher同样,执行下watcher的get方法,调用pushTarget
将当前user watcher赋值给Dep.target,get()中value = this.getter.call(vm, vm)
这个语句会触发该自定义watcher监听的响应式属性的get方法,并将当前的user watcher推入该属性依赖的subs中,因此当user watcher监听的属性set触发后,通知订阅该依赖的watcher去触发update,也就是触发该watch绑定的key对应的handler。而后就是调用popTarget出栈并赋值给Dep.target。dom
initState初始化工做大体到这里过,接下去会执行$mount开始渲染工做
$mount主要工做:new了一个渲染Watcher,并将updateCompent做为callback传递进去并执行函数
updateComponent = () => { vm._update(vm._render(), hydrating) } new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */)
三种watcher中new Watcher的时候,只有computed watcher不会一开始就执行它的get()方法。$mount里面new的这个render watcher会调用get()
方法,调用pushTarget
将当前render watcher赋值给Dep.target。接下去重头戏来了,调用updateComponent
,该方法会执行vm._update(vm._render(), hydrating)
,其中render函数会触发html中使用到的响应式属性的get钩子。get钩子会让该响应式属性的依赖实例dep将当前的render watcher推入其subs数组中,因此当依赖的响应式属性改变以后,会遍历subs通知订阅它的watcher去调用update()。oop
可能你们对watcher和dep调来调去一头雾水,我讲个实例this
<div id="app"> <div>{{a}}</div> <div>{{b}}</div> </div> new Vue({ el: "#app", data() { return { a:1, } }, computed:{ b() { return a+1 } }, })
我直接从渲染开始讲,只讲跟dep跟watcher有关的
$mount:new一个渲染watcher(watcher的get方法中会将渲染watcher赋值给Dep.target)的时候会触发 vm._update(vm._render(), hydrating)
,render的时候会获取html中用到的响应式属性,上面例子中先用到了a,这时会触发a的get钩子,其中dep.depend()
会将当前的渲染watcher推入到a属性的dep的subs数组中。
接下去继续执行,访问到b(b是计算属性的值),会触发计算属性的get方法。计算属性的get方法是调用createComputedGetter
函数后的返回函数computedGetter
,computedGetter
函数中会执行watcher.depend()
。Watcher的depend方法是专门留给computed watcher使用的。刚才上面说过了除了computed watcher,其余两种watcher在new 完以后都会执行他们的get方法,那么computed watcher在new完以后干吗呢,它会new一个dep。回到刚才说的专门为computed watcher开设的方法watcher.depend()
,他的做用是执行this.dep.depend()
(computed watcher定义的dep就是在这里使用到的)。this.dep.depend()
会让当前的渲染watcher订阅该计算属性依赖,该计算属性也会将渲染watcher推入到它本身的subs([render watcher])中,当计算属性的值修改以后会通知subs中的watcher调用update()
,因此计算属性值变了页面能刷新。回到前面说的触发b计算属性的get钩子那里,get钩子最后会执行watcher.evaluate()
,watcher.evaluate()
会执行computed watcher的get()
方法。这时候重点来了,会将Dep.target(render watcher)推入targetStack栈中(存入以后以便待会儿取出继续用),而后将这个计算属性的computed watcher赋值给Dep.target。get方法中value = this.getter.call(vm, vm)
,会执行computed属性绑定的handler。如上面例子中return a + 1。使用了a那么就必定会触发a的get钩子,get钩子又会调用dep.depend()
,dep.depend()会让computed watcher将dep存入它的deps数组中,a的dep会将当前的Dep.target(computed watcher)存入其subs数组中,当前例子中a的subs中就会是[render watcher,computed watcher],因此a值变化会遍历a的subs中的watcher调用update()
方法,html中用到的a会刷新,计算属性watcher调用update()
方法会通知他本身的subs([render watcher])中render watcher去调用update方法,html中用到的计算属性b才会刷新dom(这里提个醒,我只是粗略的讲,计算属性依赖的属性变化后他不必定会触发更新,他会比较计算完以后的值是否变化)。computed watcher的get()方法最后会调用popTarget()
,将以前存入render watcher出栈并赋值给Dep.target,这时候我例子中targetStack就变成空数组了。render watcher的get方法执行到最后也会出栈,这时候会将Dep.target赋值会空。