一、事情起因
- 1.1 自己0-1搭建的项目是
Vue3 + Vite(4.4.11) + pinia(2.1.7) + echarts(5.4.3)
, 项目打包后控制台提示了一个小警告,强迫症患者这谁受得了啊?必须盘他! - 1.2 仔细一看,大概的意思是 打包之后的产物 index.js 体积超过了默认的500kb,使用build.rollupOptions.output.manualChunks 来改进分块,或者通过build.chunkSizeWarningLimit 调整此警告的区块大小限制。 也就是给 build.chunkSizeWarningLimit 设置一个大一点的值,官方默认是500kb提示警告;
- 1.3 但我猜机智的你一定想问,有没有别的方式,不让他提示这个警告,从源头上解决这个问题,要知道,不管什么项目,打包之后的体积越大,意味着上传到服务器后,网络传输时间越久,从而一定程度上影响了我们的性能,于是就有了【拆包】的说法。
二、什么是拆包/分包?
-
2.1 分包(code-splitting)是一种优化技术,用于将应用程序的代码拆分成多个较小的包(或块)。这种技术的目的是减少初始加载时需要下载的代码量,以提高应用程序的性能和加载速度。我们这里用的Vite,Vite底层本身用的rollupjs,这边重点阐述Vite的拆包方式。在使用 build.rollupOptions.output.manualChunks 时,Rollup官方拆包方式
-
2.2 该选项允许你创建自定义的公共 chunk。当值为对象形式时,每个属性代表一个 chunk,其中包含列出的模块及其所有依赖,除非他们已经在其他 chunk 中,否则将会是模块图(module graph)的一部分。chunk 的名称由对象属性的键决定。
javascript
manualChunks: {
lodash: ['lodash']
}
-
2.3 上述例子中,即使你只是使用 import get from 'lodash/get' 形式引入,Rollup 也会将 lodash 的所有模块放到一个自定义 chunk 中。
-
2.4 当该选项值为函数形式时,每个被解析的模块都会经过该函数处理。如果函数返回字符串,那么该模块及其所有依赖将被添加到以返回字符串命名的自定义 chunk 中。例如,以下例子会创建一个命名为 vendor 的 chunk,它包含所有在 node_modules 中的依赖:
javascript
function manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}
-
安装包大小分析插件,来看下你项目中那些内容占了主要的空间;
npm install --save-dev rollup-plugin-visualizer
或者
yarn add --dev rollup-plugin-visualizer
-
vite.config.js 中 引入
javascript
import { visualizer } from "rollup-plugin-visualizer";
module.exports = {
plugins: [
visualizer({
open: true, // 注意这里要设置为true,否则无效
gzipSize: true, // 分析图生成的文件名
brotliSize: true, // 收集 brotli 大小并将其显示
filename: "stats.html", // 分析图生成的文件名
}),
],
};
- 2.5 配置完执行一下
npm run build
, 项目根目录会出现一个stats.html
的文件, 用浏览器打开可以看到,目前暂用整个项目体积最大的是echarts和ant-design-vue,接下来就是想办法把他们单独拆成一个独立的文件,并且上传到CDN上面,通过配置baseUrl, 打包结果中的 index.html 只需引入这些独立的文件即可;
三、Vite如何拆包/分包?
-
3.1 如果是Vite 2.9以下版本,那你恭喜你,用上面Rollup官方文档配置方式即可,但是请看官方文档
-
3.2 到 Vite2.8 时,默认的策略是将 chunk 分割为 index 和 vendor。这对一些 SPA 来说是好的策略,但是要对每一种用例目标都提供一种通用解决方案是非常困难的。从 Vite2.9 起,manualChunks 默认情况下不再被更改。你可以通过在配置文件中添加 splitVendorChunkPlugin 来继续使用 "分割 Vendor Chunk" 策略:
-
3.3 啊这?splitVendorChunkPlugin() 是个啥?我要怎么配置?会不会比rollup官方配置更香呢?管他三七二十一,不让我用我偏用!
javascript
build: {
assetsDir: "",
chunkSizeWarningLimit: 1500,
emptyOutDir: true,
rollupOptions: {
output: {
dir: "dist/",
format: "es", // es iife
inlineDynamicImports: false,
// 配置方式一:
// manualChunks: {
// echarts: ['echarts', 'echarts-wordcloud'],
// // echartsWordcloud: ['echarts-wordcloud'],
// antDesignVue: ['ant-design-vue'],
// vue: ['vue', 'vue-router'],
// axios: ['axios'],
// dayjs: ['dayjs'],
// pinia: ['pinia'],
// xlsx: ['xlsx', 'xlsx-js-style']
// },
// 配置方式二:
manualChunks(id) {
// 判断是否为第三方依赖,将其拆分到 vendor 中
if (id.includes('node_modules')) {
// 这里代码可以优化一下,但是我懒,我相信你一定可以的!
if (id.includes('ant-design-vue')){
return 'ant-design-vue';
} else if (id.includes('echarts') || id.includes('echarts-wordcloud')){
return 'echarts';
} else if (id.includes('dayjs')){
return 'dayjs';
} else if (id.includes('lodash-es')){
return 'lodash-es';
} else if (id.includes('zrender')){
return 'zrender';
} else if (id.includes('xlsx') || id.includes('xlsx-js-style')){
return 'xlsx';
} else {
return 'vendor';
}
} else {
return 'index';
}
},
},
plugins: [
cleaner({
targets: [
'dist/'
]
}),
]
},
cssCodeSplit: false,
}
- 3.4 来看下打包结果,整体来看效果还不错,之前包含在 index中的 echarts、ant-design-vue、xlsx 等都已经打包到独立的文件夹;
- 3.5 打包结果中的index中也是以link的方式引入,如果在base中配置了前缀,打包后自动上传到CDN,每次只需要上传一个index.html 和样式文件即可;
- 3.6 分析打包结果,最大的两个第三方包 ant-desin-vue、echarts 已经变成了一个独立的文件夹,大多数情况这类第三方包是不会变的,如果放到我们的CDN或者文件服务器上,每次打包后只需要引入即可,第二次打包过滤掉这类包,这大大提升了打包速度,同时也加快了我们的网站性能!散会!