Vue 学习笔记(二十七):Vue CLI 构建

Build Targets
构建目标

vue-cli-service build 通过 --target 指定不同构建目标

应用 App

默认是应用模式:

  • index.html 带有注入的资源和 resource hint
  • 第三方库被分到一个独立包以便更好的缓存
  • 小于 4kb 的静态资源被内联在 JavaScript 中
  • public 中的静态资源被复制到输出目录中

库 Library

注意对 Vue 的依赖
在库模式中,Vue 是外置的,包中没有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。

解决:在build命令中添加--inline-vue标志

1
vue-cli-service build --target lib --inline-vue

将一个单独的入口构建为一个库:

1
vue-cli-service build --target lib --name myLib [entry]
1
2
3
4
5
6
File                     Size                     Gzipped

dist/myLib.umd.min.js 13.28 kb 8.42 kb
dist/myLib.umd.js 20.95 kb 10.22 kb
dist/myLib.common.js 20.57 kb 10.09 kb
dist/myLib.css 0.33 kb 0.23 kb

入口可以是一个 .js 或一个 .vue 文件。如果没有指定入口,则会使用 src/App.vue

构建一个库的输出:

  • dist/myLib.common.js:一个给打包器用的 CommonJS 包 (不过,webpack 目前还并没有支持 ES modules 输出格式的包)

  • dist/myLib.umd.js:一个直接给浏览器或 AMD loader 使用的 UMD 包

  • dist/myLib.umd.min.js:压缩后的 UMD 构建版本

  • dist/myLib.css:提取出来的 CSS 文件 (可以通过在 vue.config.js 中设置 css: { extract: false } 强制内联)

注意
开发一个库或多项目仓库 (monorepo)时请注意导入 CSS 是具有副作用的。请确保在 package.json 中移除 "sideEffects": false,否则 CSS 代码块会在生产环境构建时被 webpack 丢掉。

Vue vs. JS/TS 入口文件 Entry Files

使用一个 .vue 文件作为入口时,你的库会直接暴露这个 Vue 组件本身,因为组件始终是默认导出的内容。 When using a .vue file as entry, your library will directly expose the Vue component itself, because the component is always the default export.

当使用一个 .js.ts 文件作为入口时,它可能会包含具名导出,所以库会暴露为一个模块。(when you are using a .js or .ts file as your entry, it may contain named exports, so your library will be exposed as a Module.)也就是说你的库必须在 UMD 构建中通过 window.yourLib.default 访问,或在 CommonJS 构建中通过 const myLib = require('mylib').default 访问。如果没有任何具名导出并希望直接暴露默认导出,可以在 vue.config.js 中使用以下 webpack 配置:(If you don’t have any named exports and wish to directly expose the default export, you can use the following webpack configuration)

1
2
3
4
5
6
7
module.exports = {
configureWebpack: {
output: {
libraryExport: 'default'
}
}
}

Web Components 组件

在 Web Components 模式中,Vue 是外置的。包中不会有 Vue,即使代码中导入了 Vue。这里的包会假设在页面中已经有一个可用的全局变量 Vue

将一个单独的入口构建为一个 Web Components 组件:

1
vue-cli-service build --target wc --name my-element [entry]

注意这里的入口应该是一个 *.vue 文件。Vue CLI 将会把这个组件自动包裹并注册为 Web Components 组件,无需在 main.js 里自行注册。也可以在开发时把 main.js 作为 demo app 单独使用。

该构建将会产生一个单独的 JavaScript 文件 (及其压缩后的版本) 将所有的东西都内联起来。当这个脚本被引入网页时,会注册自定义组件 <my-element>,其使用 @vue/web-component-wrapper 包裹了目标的 Vue 组件。这个包裹器(wrapper)会自动代理属性properties、特性attributes、事件events和插槽slots

注意这个包依赖了在页面上全局可用的 Vue

这个模式允许你的组件的使用者以一个普通 DOM 元素的方式使用这个 Vue 组件:

1
2
3
4
5
<script src="https://unpkg.com/vue"></script>
<script src="path/to/my-element.js"></script>

<!-- 可在普通 HTML 中或者其它任何框架中使用 -->
<my-element></my-element>

注册多个 Web Components 组件的包

使用一个 glob 表达式作为入口指定多个组件目标:

1
vue-cli-service build --target wc --name foo 'src/components/*.vue'

构建多个 web component 时,--name 将会用于设置前缀,同时自定义元素的名称会由组件的文件名推导得出the custom element name will be inferred from the component filename。比如 HelloWorld.vue 的组件携带 --name foo 将会生成的自定义元素名为 <foo-hello-world>

异步 Web Components 组件

Async Web Component

指定多个 Web Components 组件作为目标时,这个包可能会变得非常大,并且用户可能只想使用你的包中注册的一部分组件。这时异步 Web Components 模式会生成一个 code-split 的包,带一个只提供所有组件共享的运行时,并预先注册所有的自定义组件小入口文件。一个组件真正的实现只会在页面中用到自定义元素相应的一个实例时按需获取:The async web component mode produces a code-split bundle with a small entry file that provides the shared runtime between all the components, and registers all the custom elements upfront. The actual implementation of a component is then fetched on-demand only when an instance of the corresponding custom element is used on the page

1
vue-cli-service build --target wc-async --name foo 'src/components/*.vue'
1
2
3
4
5
6
7
8
File                Size                        Gzipped

dist/foo.0.min.js 12.80 kb 8.09 kb
dist/foo.min.js 7.45 kb 3.17 kb
dist/foo.1.min.js 2.91 kb 1.02 kb
dist/foo.js 22.51 kb 6.67 kb
dist/foo.0.js 17.27 kb 8.83 kb
dist/foo.1.js 5.24 kb 1.64 kb

现在用户在该页面上只需要引入 Vue 和这个入口文件即可:

1
2
3
4
5
<script src="https://unpkg.com/vue"></script>
<script src="path/to/foo.min.js"></script>

<!-- foo-one 的实现的 chunk 会在用到的时候自动获取 -->
<foo-one></foo-one>