项目为 9 个入口的多页应用, @vue/cli-service
版本为 4.4.4
,webpack
版本为 4.46.0
。部署的每一个应用加载都巨慢无比,抓包发现每个应用的首页都需要加载十几 M 的 js 文件。
优化前:
优化后:
通过命令./node_modules/.bin/vue-cli-service inspect --mode prod > webpack-output.txt
可以看到默认的分包策略:
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
这样会导致 node_modules 中使用的包全都打包进了 chunk-vendors.js 中,而每个应用都使用了同一个 chunk-vendors.js 文件
调整打包策略,将引用的次数达到 5 次的 npm 包打包进 chunk-vendors 中,引用次数达到 2 次的资源(业务代码,图片等)打包进 chunk-common 中:
config.optimization.splitChunks = {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
minChunks: 5,
priority: -9,
chunks: 'all',
// maxSize: 1000000, // chunk-vendors 再分包(生成的 chunk 没有插入 html 中,先关闭)
reuseExistingChunk: true,
},
common: {
name: 'chunk-common',
test(module) {
return module.resource && !module.resource.includes('node_modules');
},
minChunks: 2,
priority: -10,
chunks: 'all',
// maxSize: 1000000, // chunk-common 再分包(生成的 chunk 没有插入 html 中,先关闭)
reuseExistingChunk: true,
},
},
};
minChunks 不固定,通过不断调整并观察打包后的情况,达到一个比较平衡的状态。
配置 webpack externals,将 chunk-vendors 中的包以 cdn 引入:
const envCdn = {
PROD: {},
DEV: {
js: [
'https: //公司cdn镜像服务/fastly.jsdelivr.net/npm/vconsole@3.12.0/dist/vconsole.min.js',
],
},
};
const {
externals = {},
js = [],
css = [],
} = process.env.VUE_APP_CLIENT_MODE === 'PROD' ? envCdn['PROD'
] : envCdn['DEV'
];
const cdn = {
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
vconsole: 'VConsole',
echarts: 'echarts',
jsrsasign: 'jsrsasign',
swiper: 'Swiper',
...externals,
},
js: [
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js',
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js',
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js',
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/jsrsasign@8.0.24/lib/jsrsasign-all-min.js',
'https: //公司cdn镜像服务/cdn.jsdelivr.net/npm/swiper@5.4.5/dist/js/swiper.min.js',
...js,
],
css: [...css
],
};
...
pages: {
index: {
entry: 'src/main.ts',
template: 'public/index.html',
filename: 'index.html',
title: '',
cdn,
},
client: {
entry: 'client/main.ts',
template: 'public/client.html',
filename: 'client.html',
title: '',
cdn,
},
echart: {
entry: 'plugin/echart/main.ts',
template: 'plugin/echart/echart.html',
filename: 'echart.html',
title: '',
cdn: {
...cdn,
js: [
...cdn.js,
'https://公司cdn镜像服务/cdn.jsdelivr.net/npm/echarts@5.0.1/dist/echarts.min.js',
],
},
},
}
...
调整 html 模板:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0" />
<meta name="format-detection" content="telephone=no" />
<title></title>
<!-- 使用CDN的CSS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="reload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
</head>
<body>
<div id="app"></div>
<!-- 使用CDN的JS文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<% if (VUE_APP_CLIENT_MODE !== 'PROD') { %>
<script>
if(location.host.indexOf('localhost') === -1) {
const vConsole = new VConsole();
}
</script>
<% } %>
</body>
</html>
另外,生产打包的时候, NODE_ENV 环境变量设置为 prod
,而不是 production
,导致部分包 tree shaking
未生效,引入了整个包。