代码分割
-
更好地按需加载
-
更好的利用缓存
-
提高页面加载性能
在生产环境下,为了提高页面加载性能,构建工具一般将项目的代码打包(bundle)到一起,这样上线之后只需要请求少量的 JS 文件,大大减少 HTTP 请求
- 但随着前端工程的越来越复杂,单份的打包产物体积越来越庞大,会出现一系列应用加载性能问题,而代码分割可以很好地解决它们
bundle、chunk、vendor 构建领域的专业概念
bundle指的是整体的打包产物,包含 JS 和各种静态资源。chunk指的是打包后的 JS 文件,是bundle的子集。vendor是指第三方包的打包产物,是一种特殊的 chunk
代码分割code splitting解决了什么问题?
在传统的单 chunk 打包模式下,当项目代码越来越庞大,最后会导致浏览器下载一个巨大的文件,从页面加载性能的角度来说,主要会导致两个问题:
- 无法做到按需加载,即使是当前页面不需要的代码也会进行加载。
- 线上缓存复用率极低,改动一行代码即可导致整个 bundle 产物缓存失效。
Code Splitting可以将按需加载的代码拆分出单独的 chunk,避免了冗余的加载过程,使页面性能得到提升。
Code Splitting可以更好地利用缓存,让浏览器复用本地的强缓存,大大提升线上应用的加载性能
打包完产物体积太大,怎么拆包?
Vite 默认拆包
- Vite 默认拆包的优势在于实现了 CSS 代码分割与业务代码、第三方库代码、动态 import 模块代码三者的分离
- 缺点也比较直观,第三方库的打包产物容易变得比较臃肿,
vendor.js的体积大小会随着项目复杂程度变得越来越大- 显然是有进一步拆包的优化空间的,这个时候我们就需要用到 Rollup 中的拆包 API ——
manualChunks
- 显然是有进一步拆包的优化空间的,这个时候我们就需要用到 Rollup 中的拆包 API ——
这个默认拆包策略是以插件的方式存在的,由于 2.9之后就不使用该策略了,所以如果还要用的话就得自己导入插件进行使用:
import { splitVendorChunkPlugin } from 'vite'
export default defineConfig({
plugins: [splitVendorChunkPlugin()]
})
可以通过配置
build.rollupOptions.output.manualChunks来自定义 chunk 分割策略(查看 Rollup 相应文档)从 Vite 2.9 起,
manualChunks默认情况下不再被更改。你可以通过在配置文件中添加splitVendorChunkPlugin来继续使用 “分割 Vendor Chunk” 策略
splitVendorChunkPlugin()插件的主要作用是在构建过程中输出关于拆分情况的警告信息,并可以通过配置的方式对拆分行为进行控制
实现自定义拆包
vite在生产环境是通过rollup来打包, 提供了manualChunks来实现自定义拆包策略
manualChunks在对象格式的配置中,key代表 chunk 的名称,value为一个字符串数组,每一项为第三方包的包名
自定义拆包之后,原来的 vendor 大文件被拆分成了我们手动指定的几个小 chunk
- 当第三方包更新的时候,也只会更新其中一个 chunk 的
- 而不会全量更新,从而提高了对第三方模块的缓存
// vite.config.ts
{
build: {
rollupOptions: {
output: {
// manualChunks 配置
manualChunks: {
// 将 React 相关库打包成单独的 chunk 中
'react-vendor': ['react', 'react-dom'],
// 将 Lodash 库的代码单独打包
'lodash': ['lodash-es'],
// 将组件库的代码打包
'library': ['antd', '@arco-design/web-react'],
},
},
}
},
}
拆包会出现的问题
- 容易导致循环引用
如何解决拆包导致循环引用的问题?
比如将react包单独拆包
- 确定 react 相关包的入口路径
- 在 manualChunks 中拿到模块的详细信息,向上追溯它的引用者,如果命中 react 的路径,则将模块放到 react-vendor中
vite集成拆包插件
- vite-plugin-chunk-split 是一个 Vite 插件,支持多种拆包策略,可避免手动操作
manualChunks潜在的循环依赖问题
// vite.config.ts
import { chunkSplitPlugin } from 'vite-plugin-chunk-split';
export default {
chunkSplitPlugin({
// 指定拆包策略
customSplitting: {
// 1. 支持填包名。`react` 和 `react-dom` 会被打包到一个名为`render-vendor`的 chunk 里面(包括它们的依赖,如 object-assign)
'react-vendor': ['react', 'react-dom'],
// 2. 支持填正则表达式。src 中 components 和 utils 下的所有文件被会被打包为`component-util`的 chunk 中
'components-util': [/src\/components/, /src\/utils/]
}
})
}