在 Vue 實例化的 initState
中, mount 階段透過 mountComponent
執行,這裡把 Watcher
實例化
// src/core/instance/lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// ...
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true)
//...
}
watcher
會先執行 this.get()
// src/core/observer/watcher.js
let uid = 0
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
computed: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
dep: Dep;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
//...
/********************************************************
把傳入的 updateComponent function 賦值給 this.getter
*********************************************************/
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = noop
process.env.NODE_ENV !== 'production' && warn(
`Failed watching path: "${expOrFn}" ` +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
)
}
}
/********************************************************
執行 this.get()
*********************************************************/
this.value = this.lazy
? undefined
: this.get()
}
get () {
/*************************************************
this 就是 Watcher,把自己賦值給 Dep.target
**************************************************/
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
/*************************************************
把接收的 dep 物件 push 到 Watcher.newDeps 中
**************************************************/
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
/*************************************************
調用 dep 的 addSub 傳入一個 Watcher 物件
**************************************************/
dep.addSub(this)
}
}
}
// ...
}