vue的不同构建版本的解释(阉割版vs完整版)

3,500 阅读2分钟

vue的不同构建版本的解释

前言:最近在做vue2组件库升级到vue3,有些高级组件用到了 组件选项对象 <component :is="{ 组件选项对象 }"></component> ,并且用到vue2的 extend,也支持组件选项对象。使用类似下面

// 创建构造器 
var Profile = Vue.extend({  // 组件选项对象
    template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', 
    data: function () { 
        return {
            firstName: 'Walter', 
            lastName: 'White', 
            alias: 'Heisenberg' 
        } 
    } 
}) 
// 创建 Profile 实例,并挂载到一个元素上。 
new Profile().$mount('#mount-point')

问题是:升级到vue3后,发现并不支持了,感觉很难受,因为封装了组件选项的组件特别好用。 后面发现,引入另一个vue的构建版本就可以搞定了!!

解法也很简单,配置一下vue的构建版本即可。目前我用的是vite。用webpack配置文件也是一样的写法

// 找到 vite.config.js 或 webpack。config.js 或 vue.config.js

// 找到 resolve属性, 配置别名
resolve: {
  alias: {
     vue: 'vue/dist/vue.esm-bundler.js' // 增加这一行 , 增加动态编译的能力
  }
}

要解释为什么这样就可以,需要理解vue的构建版本,首先需要知道vue的默认构建版本是什么?

vue的构建版本解释

问题:import { createApp } from 'vue'(vue3的引入), from后面跟的是 vue哪个版本? 如果不用import不用打包,直接在script标签内用呢?vue是引入哪个构建版本?

  • 打开vue的源码dist包 或 找vue的CDN,会有多个构建版本。默认是什么?分别是在什么场景下去用?
// 服务端渲染。 通过 `require()` 在 Node.js 服务器端渲染使用。
vue.cjs.js
vue.cjs.prod.js

// 使用构建工具,如 `webpack`,`rollup` 和 `parcel` 等打包出来的工程项目
vue.esm-bundler.js
vue.runtime.esm-bundler.js

// 通过浏览器中的 `<script src="...">` 直接使用,暴露全局Vue
vue.global.js
vue.global.prod.js
vue.runtime.global.js
vue.runtime.global.prod.js

// 在浏览器中通过 `<script type="module">` 来使用(浏览器原生 ES 模块导入使用)
vue.esm-browser.js
vue.esm-browser.prod.js
vue.runtime.esm-browser.js
vue.runtime.esm-browser.prod.js

我们使用vue可能会有4种场景:

  1. 通过浏览器中的 <script src="..."> 直接使用,暴露全局Vue

    • 使用带global关键字的 **.global.**.js
    • 提示:全局打包不是 UMD 构建的,它们被打包成 IIFEs,并且仅用于通过 <script src="..."> 直接使用。
  2. 在浏览器中通过 <script type="module"> 来使用(浏览器原生 ES 模块导入使用)

    • 使用带esm-browser关键字的 **.esm-browser.**.js
  3. 使用构建工具,如 webpackrollup 和 parcel 等打包出来的工程项目

    • 使用带esm-bundler关键字的 **.esm-bundler.**.js

    • 以下是 解决我上面提的问题,import { createApp } from 'vue'(vue3的引入) from后面默认 跟的是 vue哪个版本?

      • vue.runtime.esm-bundler.js  (默认)  

      • 特点:

        • vue.runtime.esm-bundler.js 是 仅运行版本(阉割版,体积小,不带动态编译的能力)。 要求所有模板(<template>..</template>)都要预先编译(编译.vue文件)

          • 用vue-loader打包的过程中就编译了(<template>..</template> 转成 render(..)
        • 构建工具的默认入口: package.json 中的 module 字段

        • vue.esm-bundler.js是:完整版(无阉割的,体积会大一些。能在打包编译后,还能识别<template>..</template>模板代码,拥有编译模板的能力

  4. 服务端渲染。 通过 require() 在 Node.js 服务器端渲染使用。

    • 使用带cjs关键字的 **.cjs.**.js

细心的小伙伴应该有疑问, runtime 和 prod 是啥意思? 有什么区别? 比如

vue.esm-browser.js
vue.esm-browser.prod.js
vue.runtime.esm-browser.js
vue.runtime.esm-browser.prod.js
  1. prod:这个很好理解,就是生产环境使用的压缩版
  2. runtime:表示 仅运行版本(阉割版,体积小,不带动态编译的能力)。 要求所有模板(<template>..</template>)都要预先编译(编译.vue文件)
    • 用vue-loader打包的过程中就编译了(<template>..</template> 转成 render(..)
    • 但是如果,某些vue的api会用到动态编译的能力,比如 extend,那么这个阉割版就不适用了。需要用不带runtime的完整版
  • 比如文章的开头,Vue.extend需要动态编译<template>..</template>模板的能力,所以需要使用不带runtime关键字的构建版本 以下是vue.extend的用法,感受一下运行时 动态编译

    // 创建构造器 
    var Profile = Vue.extend({  // 组件选项对象
        template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', 
        data: function () { 
            return {
                firstName: 'Walter', 
                lastName: 'White', 
                alias: 'Heisenberg' 
            } 
        } 
    }) 
    // 创建 Profile 实例,并挂载到一个元素上。 
    new Profile().$mount('#mount-point')
    

    另外 render 方法是底层生成vnode的api,在任何一个vue的构建版本都需要,所以render方法不需要编译器

    // 如果需要在客户端上编译模板 (即:将字符串传递给 template 选项,或者使用元素的 DOM 内 HTML 作为模板挂载到元素),你将需要编译器,因此需要完整的构建版本:
    
    // 需要编译器   使用不带runtime关键字的构建版本
    Vue.createApp({
      template: '<div>{{ hi }}</div>'
    })
    
    // 不需要编译模板,可以用带runtime的阉割版
    Vue.createApp({
      render() {
        return Vue.h('div', {}, this.hi)
      }
    })
    

总结:

  1. vue的构建版本为了适应不同的环境去使用,总的有4种版本

  2. 构建版本的名字中:除了4种环境外,还有2个关键字:prd和runtime

  • prd好理解,是压缩版,体积小

  • runtime版,可以理解为:阉割版,体积小,不带动态编译模板的能力

    • 需要编译模板的能力的话,比如vue.extend,那么要用不带runtime的完整版,体积会稍微大一些
  1. 编译模板是什么意思?

    比如,vue需要把<template>..</template>内的html代码,生成虚拟dom树,那么需要编译器,去解析html代码(可以理解为,把html 转成 js 代码,需要编译器,编译器是一个额外的引入),比如:

    模板

    const profile = (
      <div>
        <img src="avatar.png" className="profile" />
        <h3>{[user.firstName, user.lastName].join(" ")}</h3>
      </div>
    );
    

    编译后

    import { jsx as _jsx } from "react/jsx-runtime";
    import { jsxs as _jsxs } from "react/jsx-runtime";
    
    const profile = _jsxs("div", {
      children: [
        _jsx("img", {
          src: "avatar.png",
          className: "profile",
        }),
        _jsx("h3", {
          children: [user.firstName, user.lastName].join(" "),
        }),
      ],
    });
    

    正常我们使用vue-loader,就是在打包阶段 编译好。所以默认是带runtime的阉割版,体积小


码字不易,点赞鼓励!