Vue.prototype.$mount
- 由上篇分析可知,我们将 render 函数挂载到 options 后调用了 mount 函数,这个函数在开始就定义了,实际上为
Vue.prototype.$mount
函数 - 这个原型上的 $mount 函数从何而来呢,在
platforms/web/runtime/index.js
中有定义,我们会发现这个函数实际上是去调用了 mountComponent 方法
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
- 看到这里你可能会很困惑,为什么会写两个 $mount 方法呢
实际上我们现在分析的是 with-compiler 的源码,即我们写模板文件然后通过
entry-runtime-with-compiler
将 template 编译成 render 函数再去执行runtime/index
中的 $mount 方法,如果不是 with-compiler 的 vue 的话 webpack 和 loader 就会处理将 template 编译成 render 函数,然后执行runtime/index
中的 $mount 方法,所以entry-runtime-with-compiler
中的 $mount 方法实际上做的就是 webpack 和 loader 的事情,不过是在编译阶段处理的
mountComponent
- 在
core/instance/lifecycle.js
中定义了 mountComponent 方法
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
}
callHook(vm, 'beforeMount')
let updateComponent
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true)
hydrating = false
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
- 这个方法先判断 vm.$options.render 是否存在,如果不存在的话就让它等于 createEmptyVNode
- 接着定义了 updateComponent 函数
- 创建了一个渲染 Watcher
Watcher
- 我们在
core/observer/watcher.js
中查看 class Watcher
// 我们看下 Watcher 的构造函数
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
vm._watchers.push(this)
if (options) {
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
this.before = options.before
} else {
this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid
this.active = true
this.dirty = this.lazy
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString()
: ''
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = noop
}
}
this.value = this.lazy
? undefined
: this.get()
}
- 在 mountComponent 中 new Watcher 的传参
vm: vm,
expOrFn: updateComponent,
cb: noop,
options: 一个对象,
isRenderWatcher: true
- 分析得知
vm._watcher = this
,this.getter = updateComponent
最后执行了this.get()
- get 函数
value = this.getter.call(vm, vm)
其实就是执行了updateComponent
get () {
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
}
- 所以 mountCompnent 最主要就是实例化了渲染 Watcher 并调用了 updateComponent, 即
vm._update(vm._render(), hydrating)
demo 调试
-
实例化渲染 Watcher
-
执行 get 函数,实际上调用了updateComponent
- 也就是说执行了
vm._update(vm._render(), hydrating)
- vm._update 和 vm._render 又做了哪些事情呢?且听下回分解