各种初始化阶段完成之后,调用了vm.$mount
这个阶段的主要内容是,获取用户传入的模板内容,并将其编译成渲染函数
模板编译阶段并不是存在于Vue的所有构建版本中,它只存在于完整版(即vue.js)中。在只包含运行时版本(即vue.runtime.js)中并不存在该阶段,这是因为当使用vue-loader或vueify时,*.vue文件内部的模板会在构建时预编译成渲染函数,所以是不需要编译的,从而不存在模板编译阶段
$mount
只包含运行时版本的$mount代码如下:
Vue.prototype.$mount = function (el,hydrating) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
完整版本的$mount代码如下
var mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (el,hydrating) {
// 省略获取模板及编译代码
return mount.call(this, el, hydrating)
}
完整版的vm.$mount方法定义位于源码的src/platforms/web/entry-runtime-with-compiler.js中
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
/* 调用query方法,也就是说,如果el是个字符串,那就document.querySelector,
否则就直接返回
*/
el = el && query(el)
/* istanbul ignore if */
/* 不能直接挂载到body或者HTML上上 */
/*
Vue模板中的内容将会替换el 对应的DOM元素
如果是body或者HTML会破坏文档流
*/
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}
const options = this.$options
// resolve template/el and convert to render function
/*
如果没有写render,就获取template,编译成render函数
*/
if (!options.render) {
let template = options.template
if (template) {
/* 如果写了template 那么走下面的 */
/* 如果手写了,就用下面这个值, 如果没有手写,那么就用el 去获取 */
if (typeof template === 'string') {
/* 如果变量template存在,则接着判断如果template是字符串并且以##开头,
则认为template是id选择符 */
if (template.charAt(0) === '#') {
/* 用idToTemplate函数获取到选择符对应的DOM元素的innerHTML作为模板 */
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} else if (template.nodeType) {
/* 判断是不是一个DOM元素,如果是的话,就返回innerHTML */
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
/*
如果没有template这个options
那么就根据el ,获取外部模板
*/
template = getOuterHTML(el)
}
if (template) {
/* 上面模板已经准备好了,接下来,就是把模板编译成render函数 */
/* 下面就是关键的编译了 */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
/* 主要在compileToFunctions这个函数中进行的
返回render这个函数,
然后挂载到options上面
*/
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
/* mount 就是runtime里面的 $mount */
return mount.call(this, el, hydrating)
}