# React + craco + Webpack SplitChunksPlugin 抽離公用模組
###### tags: `React` `Webpack` `craco` `SplitChunksPlugin` `cacheGroups`
> [time=Thu, Feb 3, 2022 5:45 PM]
使用 webpack 編譯打包各個模組後,產生 `/dist` 目錄資料夾內容供部署至 server 上使用,客戶端在透過瀏覽器訪問此網站獲取內容,然而在獲取內容上是較消耗時間資源的。
瀏覽器使用了**緩存**的技術,搭配 Webpack 配置將資源緩存至客戶端中,避免每次的重新下載,達到降低網路流量以及服務器的壓力,使網站加載速度更快。
在 web 應用程式中,往往修改較頻繁的是我們的業務邏輯,但是第三方庫則是不會頻繁更動,這時我們可以透過 webpack 合理的劃分我們 chunk。
Webpack 中拆分 chunk 可以分為:
- **Bundle splitting**:使用 `SplitChunksPlugin` 將 bundle 拆分成許多塊,達到更好的緩存,以及平行載入。
- **Code splitting**:使用 `import()` 動態加載,當使用者需要時才進行載入
:::info
- [這裡 CRA 是使用 craco 如果不了解的可以參考這裡 >> ](https://hackmd.io/@yellow/S1mlj0yCK)
- [關於 CRA 打包後的產物可以參考這裡 >> ](https://hackmd.io/@yellow/rysIz5v0t)
:::
## 文章大意:
- SplitChunksPlugin 基本介紹
- 在 React + craco 中使用
- 嘗試打包不同的 modules
## SplitChunksPlugin 基本介紹
開箱及用的 `SplitChunksPlugin` 在 `Webpack 4` 中已預設裝載使用,可由 `optimization.splitChunks` 去做提取第三方和公共模塊的配置。
**`splitChunks` 預設配置:**
```javascript
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', // 有效值有 'all','async','initial'
minSize: 20000, // 生成 chunk 的最小體積(≈ 20kb)
minRemainingSize: 0, // 確保拆分後剩餘 chunk 的最小體積,避免產生體積為 0 的塊
minChunks: 1, // 拆分前必須共享模塊的最小 chunk 數
maxAsyncRequests: 30, // 按需加載時的最大並行請求數
maxInitialRequests: 30, // 入口點最大並行請求數,包含入口文件夾上同時加載 js 的數量
enforceSizeThreshold: 50000, // 強制拆分的體積閾值(門檻值)和其他限制 (minRemainingSize,maxAsyncRequests,maxInitialRequests) 将被忽略。
cacheGroups: { // 拆分 chunk 配置
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10, // 優先級
reuseExistingChunk: true, // 重用已存在的模塊
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
```
### `splitChunks.chunks` 的三種模式:
> 選擇哪些類型的 chunk 進行優化,可以是 `string = 'async'` 也可以是 `function (chunk)`
- ==**async (預設值)**==:只處理懶加載的 chunk,例如 `import(xxx)` 語法載入的模塊。
- ==**initial**==:只處理同步加載,例如 `import xxx`。
- ==**all**==:包含以上兩種。
### `splitChunks.cacheGroups` 緩存組(配置關鍵)拆分 chunk 配置:
> 在`cacheGroups` 可以繼承或覆蓋 `splitChunks.*` 的任何選項,並額外提供三個屬性
- ==**test**==:默認為所有 modules, 可匹配模塊路徑或 chunk 名稱,當匹配 chunk 名稱時會包含此包裡的所有 modules
- ==**priority**==:表示抽取權重,數字越大表示優先級越高。因為一個module 可能會滿足多個cacheGroups 的條件,那麼抽取到哪個就由權重最高的為主。
- ==**reuseExistingChunk**==:表示是否使用已有的chunk,如果為true 則表示如果當前的chunk 包含的模塊已經被抽取出去了,那麼將不會重新生成新的。
- **禁用任何默認緩存組:** 設置 `splitChunks.cacheGroups.default: false` 就可以了。
[更多 splitChunksPlugin 默認配置 >> ](https://webpack.docschina.org/plugins/split-chunks-plugin/#optimizationsplitchunks)
<br />
## 在 React + craco 中使用
`optimization` 是需要配置在 `webpack` 裡面,而 craco 有開放 webpack 的api 只有`alias`、`plugins` 以及 `configure`,沒有的街口就需要設定在 `configure` 裡
如下範例,先來測試一下
```javascript
...
module.exports = {
webpack: {
configure: {
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendors',
priority: 2,
enforce: true,
reuseExistingChunk: true
}
}
}
}
}
}
}
```
輸出,順利改動了
```
135.76 KB build/static/js/vendors.f2865771.chunk.js
7.02 KB build/static/css/vendors.82e83d1a.chunk.css
953 B build/static/js/main.73e2459b.chunk.js
772 B build/static/js/runtime-main.406dd86f.js
```
<br />
## 嘗試打包不同的 modules
```javascript
cacheGroups: {
reactLib: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|@reduxjs\/toolkit\/dist)[\\/]/,
name: 'react-lib',
chunks: 'all',
enforce: true,
priority: 40,
reuseExistingChunk: true
},
antLib: {
name: 'ant-lib',
test: /[\\/]node_modules[\\/](@ant-design|antd\/lib)[\\/]/,
chunks: 'all',
enforce: true,
priority: 30,
reuseExistingChunk: true
},
apolloLib: {
name: 'apollo-lib',
test: /[\\/]node_modules[\\/](@apollo\/client|graphql|graphql-tag)[\\/]/,
chunks: 'all',
enforce: true,
priority: 20,
reuseExistingChunk: true
},
commons: {
chunks: 'all',
name: 'commons',
minSize: 0, // 公用模塊的大小限制 bytes
minChunks: 5, // 公用模塊最少復用次數
maxInitialRequests: 2,
priority: 10,
reuseExistingChunk: true
},
vendors: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendors',
priority: 10,
enforce: true,
reuseExistingChunk: true
}
}
```
輸出,拆出不同包了
```
78.56 KB build/static/js/vendors.79765e58.chunk.js
42.48 KB build/static/js/react-lib.8ee700fa.chunk.js
38.97 KB build/static/js/apollo-lib.0571605e.chunk.js
32.89 KB build/static/js/ant-lib.a4f00450.chunk.js
15.23 KB build/static/css/ant-lib.cee8ab24.chunk.css
2.5 KB build/static/js/main.8997e2a3.chunk.js
771 B build/static/js/runtime-main.e042f11e.js
```
待續...
### Refernce
- [Webpack 前端打包工具 - 使用 SplitChunksPlugin 抽離公用模組](https://awdr74100.github.io/2020-04-06-webpack-splitchunksplugin/)
- [webpack高级配置-抽离公共和第三方代码](https://zhuanlan.zhihu.com/p/117563513)
- [Webpack 4 - create vendor chunk](https://stackoverflow.com/questions/48985780/webpack-4-create-vendor-chunk)
- [The 100% correct way to split your chunks with Webpack](https://medium.com/hackernoon/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758)
- [Webpack-优化(Optimization)](https://webpack.docschina.org/configuration/optimization/)