vuejs中runtime-Only与runtime-Complier的区别

1,247 阅读3分钟

写在前面

最近在看vue2源码,发现很对之前从未关注的知识点,发现vue2源码确实有很多可以发掘的地方,在此开始做一个连载,后续也会加上vue3的源码内容。那先进入第一篇: vue2中 runtime-only和runtime-compiler的区别

1、首先在看源码中遇到的问题

在开始阅读源码时,我一向是直接第一步找入口文件,然后在直接从入口文件中进入,那我们先看下vue源码中的入口文件,可以直接看package.json中的main对应的文件,毫无疑问,vue对应的入口文件就是vue.runtime.common.js

"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",

于是我进入dist目录,发现竟然有N多个js文件vue.common.js``vue.esm.js``vue.runtime.js... 于是我感觉这代表vue有多种可以引入的打包好的资源。 但是这多种vue文件是什么时候用呢? 带着这个疑问我又去找了vue的源码打包文件scripts/build.js,进入之后发现该打包是基于rollup进行的打包,其配置在scripts/config.js 中可以找到

web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),   
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
},
'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.prod.js'),
    format: 'cjs',
    env: 'production',
    banner
},
...

同时在网上找到了一份关于不同vuejs使用场景

image.png

其中 umd`` commonjs`` esmodule 分别是指该js文件导出的是对应的模块;同时full代表runtime-compiler, 因此问题出现了,什么时候使用runtimeoly, 什么时候用runtime-compiler呢?

2.两种模式的使用场景

在老版本的vue2中,当我们基于vue-cli创建vue项目时,是可以选择该创建的项目使用的vue是runtimeonly或者是runtime-compiler

image.png

但是在新版本vue-cli创建vue2甚至是vue3时,直接默认就是runtimeonly 模式

我们可以从创建的项目中的node_modules/vue/package.json中的main字段看出

main字段代表包被引用时具体的引用js路径

"main": "dist/vue.runtime.common.js",

我们知道,vue在整个模版渲染过程中,需要经历

template => ast => render函数 => virtual dom => patch => dom

而runtimeonly模式在最后的渲染阶段只会走 render => vdom => patch => dom

两者的区别在与runtimeonly在运行时已经编译完成;runtime-compiler需要在运行时才能编译

2.1 runtimeonly

runtimeonly模式是需要和外部的 webpack配合实现的

webpack基于vue-loader实现将所有的以.vue为结尾的文件编译成render函数,因此在运行时不需要再次编译,也不需要编译器,该版本不带编译器,因此文件会更小,而且执行更快, 默认也是这种方式

对应main.js中的写法(默认)

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')

2.2 runtime-compiler

runtime-compiler的出现是由于假如有template属性,那么就需要在运行时进行编译,而runtime-compiler版本是带有编译器的,因此该种情况下需要用该版本

在main.js中的写法:

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
    components: {App},
    template: '<App/>'
}).$mount('#app')

该种情况下: vue.js文件会更大,而且由于需要在运行时编译,对性能也有影响

2.3 看源码

那具体这两种模式在源码中的区别在哪里呢?

runtime-compiler比runtimeonly多了这些代码

if (!options.render) { // runtimeonly和runtime + compiler的区别在这里
    if (template) {
    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
}

可以看出runtime-compiler模式在没有render同时有template属性的时候采用

3.如何改模式

假如是因为模式问题导致的问题,一般会报错

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

现在全部vue默认是runtimeonly, 那如何在两种模式中切换呢?

有以下三种方法

3.1 直接改main.js中vue文件的引入

import Vue from 'vue' 直接改成 import Vue from 'vue/dist/vue.esm.js' 即可

3.2更改webpack配置

在vue.config.js中加入下面的配置即可

    module.exports = {
        configureWebpack: config => {
            config.resolve = {
                alias: {
                    'vue$': 'vue/dist/vue.js'
                }
            }
        },
    };

3.3 将main.js中的写法改成相匹配的即可,具体写法见上