Vue 之 运行时 vs 编译器+运行时(深入源码)

4,384 阅读2分钟

引子

最近想从零开始学习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:由于传入$mountel既可以是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处理)等等,这些在后续的学习文章中记录

推荐文献: Runtime Only和Runtime + Compiler