拆包最佳实践
良好的拆包策略对于提升应用的加载性能是十分重要的,可以充分利用浏览器的缓存机制,减少请求数量,加快页面加载速度。
在 Rsbuild 中内置了多种拆包策略,可以满足大部分应用的需求,你也可以根据自己的使用场景,自定义拆包配置。
拆包策略
Rsbuild 的拆包配置集中在 performance.chunkSplit 中。
Rsbuild 支持设置以下几种拆包策略:
split-by-experience
: 根据经验制定的拆分策略,自动将一些常用的 npm 包拆分为体积适中的 chunk。split-by-module
: 按 NPM 包的粒度拆分,每个 NPM 包对应一个 chunk。split-by-size
:根据模块大小自动进行拆分。all-in-one
: 将所有代码全部打包到一个 chunk 中。single-vendor
: 将所有 NPM 包的代码打包到一个单独的 chunk 中。custom
: 自定义拆包配置。
split-by-experience
分包策略
Rsbuild 默认采用 split-by-experience
策略,这是我们根据经验制定的策略。具体来说,当你的项目中引用了以下 npm 包时,它们会自动被拆分为单独的 chunk:
lib-polyfill.js
:包含core-js
,@swc/helpers
,tslib
。lib-axios.js
:包含axios
以及相关的包。
这种方式将常用的包进行分组,然后拆分为单独的 chunk,有助于浏览器缓存。
配置
export default {
performance: {
chunkSplit: {
strategy: 'split-by-experience',
},
},
};
注意事项
- 如果项目中没有安装或引用以上 npm 包,则不会生成相应的 chunk。
split-by-module
分包策略
将每一个 NPM 包拆分为一个单独的 chunk。
配置
export default {
performance: {
chunkSplit: {
strategy: 'split-by-module',
},
},
};
注意事项
- 这个配置会最细化地拆分 node_modules,产生大量的文件请求。
- 在使用 HTTP/2 时,由于存在多路复用,会加快资源的加载时间,并提高缓存命中率。
- 在未使用 HTTP/2 时,由于 HTTP 队头阻塞问题,会导致页面加载性能下降,请谨慎使用。
all-in-one
分包策略
此分包策略将所有源代码和第三方依赖打包在同一个 chunk 中。
配置
export default {
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
};
注意事项
- 这个配置会将构建生成的 JS 代码全部打包到一个文件里(除了 dynamic import 拆分的 chunk)。
- 单个 JS 文件的体积可能会非常大,使页面加载性能下降。
如果你需要将 dynamic import 拆分的 chunk 也打包到单个文件中,可以将 Rspack 的 output.asyncChunks 选项设置为 false
:
export default defineConfig({
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
tools: {
rspack: {
output: {
asyncChunks: false,
},
},
},
});
single-vendor
分包策略
此分包策略将第三方依赖打包在一个 chunk 中,源代码打包在另外的 chunk 中。
配置
export default {
performance: {
chunkSplit: {
strategy: 'single-vendor',
},
},
};
注意事项
- 单个 vendor 文件的体积可能会非常大,使页面加载性能下降。
split-by-size
分包策略
该策略下,设置 minSize
、maxSize
为一个固定值后,Rsbuild 会自动进行拆分,无需干预。
配置
export default {
performance: {
chunkSplit: {
strategy: 'split-by-size',
minSize: 30000,
maxSize: 50000,
},
},
};
自定义拆包
除了使用内置的拆包策略外,你也可以通过 Rsbuild 自定义拆包功能来满足更多的定制化需求。自定义拆包分为两部分:
- 自定义拆包分组
- 自定义 Rspack 的
splitChunks
配置
值得注意的是,这两种自定义拆包能力可以和内置的拆包策略一起使用,也就是说,你可以使用内置的拆包策略来拆分常用的包,然后再使用自定义拆包功能来拆分其他的包。
自定义分组
Rsbuild 支持自定义拆包分组,这样比内置拆包策略更灵活,同时比手写 Rspack 的 splitChunks
配置更简单。
比如将 node_modules 下的 axios
库拆分到 axios.js
中:
export default {
performance: {
chunkSplit: {
forceSplitting: {
axios: /node_modules[\\/]axios/,
},
},
},
};
通过 forceSplitting
配置,你可以很方便把某些模块拆分为一个 chunk。
注意事项
通过 forceSplitting
配置拆分的 chunk 会通过 <script>
标签插入到 HTML 文件中,作为首屏请求的资源。因此,请根据实际场景来进行适当地拆分,避免首屏资源体积过大。
自定义拆包配置
你可以通过 override
配置项来自定义 Rspack 的 splitChunks
配置,比如:
- 设置
minSize
为 30000,表示当模块大小小于 30000 字节时,不进行拆分。
export default {
performance: {
chunkSplit: {
override: {
chunks: 'all',
minSize: 30000,
},
},
},
};
- 将所有 CSS 文件打包到一个
styles.css
中。
export default {
performance: {
chunkSplit: {
override: {
cacheGroups: {
styles: {
name: 'styles',
minSize: 0,
chunks: 'all',
test: /\.(?:css|less|sass|scss|styl)$/,
priority: 99,
},
},
},
},
},
};
其中,override
配置会与 Rspack 的 splitChunks
配置进行合并,具体配置项请参考 Rspack - splitChunks。
使用 Dynamic Import 拆包
除了 chunkSplit
配置,使用 dynamic import 拆包也是一项重要的优化手段,它可以有效减少首屏的包体积。
关于 dynamic import
Dynamic import 是 ECMAScript 2020 引入的一个新特性,它允许你动态地加载一些 JavaScript 模块。Rsbuild 底层的 Rspack 默认支持 dynamic import,所以你可以直接在代码中使用它。
当打包工具遇到 import()
语法时,它会自动将相关的代码分割成一个新的 chunk,并在运行时按需加载。
例如,项目中有一个大的模块 bigModule.ts
(也可以是一个第三方依赖),你可以使用 dynamic import 来按需加载它:
// 在某个需要使用 bigModule 的地方
import('./bigModule.ts').then((bigModule) => {
// 使用 bigModule
});
当你运行构建命令时,bigModule.ts
就会被自动分割成一个新的 chunk,并在运行时按需加载。