引子
最近想从零开始学习Vue,用vue-cli建了个工程,在 main.js
中,写代码如下
import Vue from 'vue'
new Vue({
el: '#app',
mounted() {
console.log(this.message);
},
data: {
message: 'Hello Vue!'
},
template: '<div>{{this.message}}</div>'
})
但是,报错了。由于用cli创建项目时,使用的是runtime-only的构建方式,而在main.js
中,使用 template: '<div>{{this.message}}</div>'
。
Vue官网说
如果你需要在客户端编译模板 (比如传入一个字符串给 template 选项,或挂载到一个元素上并以其 DOM 内部的 HTML 作为模板),就将需要加上编译器,即完整版:
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
那么,引子中的案例只需要使用 render函数
来替代 template
就可以咯。
new Vue({
el: '#app',
mounted() {
console.log(this.message);
},
data: {
message: 'Hello Vue!'
},
render (createElement) {
return createElement('div', this.message)
}
})
此外,*.vue
文件也只需要 runtime-only
就可以了,原因如下:
当使用 vue-loader 或 vueify 的时候,*.vue 文件内部的模板会在构建时预编译成 JavaScript。你在最终打好的包里实际上是不需要编译器的,所以只用运行时版本即可。
Vue源码
(1)编译器+运行时
对应的 vue/dist/vue.js
文件,在执行 $mount(el, hydrating)
方法时,流程如下图所示:
- step1:由于传入
$mount
的el
既可以是string,也可以是Element,通过query(el)
把el
都转为Element - step2:判断render函数是否存在,若存在直接跳转到 step5
- step3:render函数不存在,则取
template
- step4:将
template
编译为render函数 - step5:调用运行时的mount方法(下面)
(2)运行时
对应的 vue/dist/vue.runtime.esm.js
,这里也有 $mount
方法,流程如下所示:
- step1:判断是否在浏览器下运行(因为vue也支持node服务器端渲染),若是则执行query(el)
问:之前(1)中的step 1就已经做过query(el)的操作,为什么在这里还执行?
答:对于编译器+运行时版本(compiler + runtime)的,那么是先执行(1),再执行(2);但对于运行时的版本(runtime-only)的,并不执行(1),即,运行时的版本是可以独立运行的,所以还需要query(el)啦
- 执行mountComponent,挂载组件
ps:mountComponen里面做了蛮多事情的,响应式的实现离不开它,创建Watcher,将某些对象响应化(用Object.defineProperty处理)等等,这些在后续的学习文章中记录