写在前面
Vue 模板编译的入口在哪儿?从创建实例到模板编译前Vue都做了些什么? 一文中的 步骤 - 5 揭示了答案 —— vm.$mount(vm.$options.el)
。
如果你不记得 vm 的身上什么时候挂载了 $mount 方法的话,Vue构造函数的创建过程 一文中 platforms/web/runtime/index.js 这一节会给你答案。$mount 方法并不是挂载在 vm 上,其实是在 Vue构造函数 上的。但是通过文件路径可知,这个方法属于平台集成的,因此有两种甚至更多的 $mount 写法。但是我们这只介绍 web 平台的。
模板编译准备
入口
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
运行时版本
Vue.prototype.$mount = function(el, hydrating) {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
带编译版本
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function(el, hydrating) {
el = el && query(el)
const options = this.$options
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
const {
render, staticRenderFns } = compileToFunctions(template, {
shouldDecodeNewlines,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
return mount.call(this, el, hydrating)
}
我们可以发现带编译版本和运行时版本并没有什么本质的区别,甚至还直接把运行时版本的 $mount 缓存了起来,然后又 call 调用了,只是在其中加了一些操作。至于什么操作,看这个名字也知道,带编译。
那什么叫带编译呢?
带编译
我们试想一下,一共有几种方式创建模板?
- el
// 方式1
new Vue({
el: '#app',
})
// 方式2
new Vue({
el: document.querySelector('#app'),
})
- template
// 方式1
new Vue({
template: '#app',
})
// 方式2
new Vue({
template: '<div id="app"></div>',
})
// 方式3
new Vue({
template: document.querySelector('#app'),
})
- render
new Vue({
render(h) {
return h('div#app')
},
})
简单列举一下就能发现,一共有3大类共6种创建模板的方式。
带编译的目的就是将这么多种方式统合成一种方式,即 render 方式。
那么,我问个问题,当 options 中同时存在 el / template / render 时,会渲染哪个?
const options = this.</