# 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/)