vue-cli 配置打包分析与实践

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject。

如何看 webpack 最终配置

1
2
3
4
"scripts": {
"webpack_output_dynamic": "vue inspect --mode=dynamic > output.js",
"webpack_output_production": "vue inspect --mode=production > output.js"
}

如何统计打包的时间

speed-measure-webpack-plugin 是一个专门测试 webpack 构建速度的工具,可以在终端列出所有 Loader 和 Plugin 的耗时。

1
2
3
4
5
6
7
8
9
10
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

module.exports = {
configureWebpack: smp.wrap({
output: {
...
}
})
}

webpack 打包结束后,输出的时间统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 SMP  ⏱
General output time took 51.66 secs

SMP ⏱ Plugins
DllReferencePlugin took 0.55 secs

SMP ⏱ Loaders
mini-css-extract-plugin, and
css-loader, and
sass-loader took 14.16 secs
module count = 1
css-loader, and
sass-loader took 14.15 secs
module count = 1

配置 vue.config.js

添加别名 resolve.alias

目前的配置

1
2
3
4
5
6
7
8
configureWebpack: {
resolve: {
alias: {
'@': '/Users/admin/Documents/code/gondor/src',
vue$: 'vue/dist/vue.runtime.esm.js'
},
},
}

扩展名配置 resolve.extensions

extensions 的配置原则是频率出现高的排前面,数组长度尽量小,以下是 vue-cli 中的默认配置 [‘.mjs’, ‘.js’, ‘.jsx’, ‘.vue’, ‘.json’, ‘.wasm’]

我们的文件没有 .mjs、.jsx,所以在 vue.config.js 中我这样配置会更好:

1
2
3
chainWebpack: (config) => {
config.resolve.extensions.store = new Set([".js", ".vue", ".json", ".wasm"]);
};

确定搜索目录 resolve.modules

当搜索模块的时候告诉 webpack 确定的目录文件,这样可以避免无用的检索。webpack 默认的配置是相对路径,检索当前目录的下的 node_modules,如果没有则再会去根目录下的 node_modules,直到没有最后抛错。

1
2
3
4
5
6
chainWebpack: (config) => {
config.resolve.modules.store = new Set([
path.resolve(__dirname, "node_modules"),
"node_modules",
]);
};

上面也是默认的

忽略非模块化 Library module.noParse

忽略那些文件中不含有 import, require, define 的调用,或任何其他导入机制的 library。忽略大型的 library 可以提高构建性能。下面是默认配置,请根据项目需要修改

1
2
3
4
5
configureWebpack: {
module: {
noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
},
}

动态链接库 DllPlugin

DllPlugin 和 DllReferencePlugin 实现了拆分 bundles,可以将一些不常被更新的第三方依赖预编译到一个或多个 manifest.json 中,做到一次打包就可以重复引用的效果。比如说我们 vue 全家桶、axios 等依赖,只要版本不升级就没必要多次打包,我们可以让这些依赖与业务代码分开打包。
使用方式大致分三步:

  • 利用 DllPlugin 配置预先打包出 _.manifest.json 文件。
  • 在 webpack 主配置中设置 DllReferencePlugin,告诉 webpack 使用了哪些动态链接库。
  • 在页面文件中加载所有的动态链接库。
    webpack.dll.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const path = require("path");
const DllPlugin = require("webpack/lib/DllPlugin");

module.exports = {
entry: {
// 将 Vue 相关模块打包到一个单独的动态链接库中
vue: ["vue", "vue-router", "vuex"],
// 将其他的一些不变动的第三方库打包到一个动态链接库中
polyfill: ["element-ui"],
},
output: {
// 输出动态链接库的文件名称,[name] 代表当前动态链接库的名称
// name 来自 entry 对象中的 key
filename: "[name].dll.js",
// 输出的文件都放到 dist 目录下
path: path.resolve(__dirname, "dist"),
// 动态链接库全局名称,例如 vue 就是 _dll_vue
library: "dll[name]",
},
plugins: [
new DllPlugin({
// 动态链接库的全局变量名称需要和 output.library 中的名称一致
// 代表 manifest.json 文件中的 name 字段的值
// 例如 vue.manifest.json 中的 'name': '_dll_vue'
name: "dll[name]",
// 动态链接库文件输出的文件名,例如 vue.manifest.json
path: path.join(__dirname, "dll", "[name].manifest.json"),
}),
],
};

vue.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
configureWebpack: {
plugins: [
// 告诉 webpack 使用的动态链接库
new DllReferencePlugin({
manifest: require('./dll/vue.manifest.json'),
}),
new DllReferencePlugin({
manifest: require('./dll/polyfill.manifest.json'),
}),
],
}
···
在 package.json 中增加
```js
"scripts": {
"dll": "webpack --config webpack.dll.config.js"
}

hard-source-webpack-plugin

webpack4 抛弃了使用 dll,hard-source-webpack-plugin 就是一个很好的替代者,它可为模块提供中间缓存步骤。第二次构建将明显更快。

外部扩展(external)

externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法,也是个人最提倡的采用 script 引入 CDN 资源

分环境压缩代码

在 development 环境时,我们可以关闭压缩来提升打包速度

1
2
3
4
5
6
7
8
9
chainWebpack: (config) => {
if (process.env.VUE_APP_ENV === "dynamic") {
// development
// js is not compressed
config.optimization.minimize(false);
// css is not compressed
config.plugins.delete("optimize-css");
}
};

打包后的代码质量

使用 webpack-bundle-analyzer 分析工具,在启动打包完成后,会在 8888 端口显示一个交互式可视化 bundle treemap。

其他

升级打包机配置