除了透過 webpack 的 optimization.splitChunks 來進行分塊外,也可使用 React 提供的 lazy 與 Suspense 來根據每一次 import 做分塊處理。
為什麼會需要這麼做呢?因為隨著一個 React app 的內容越來越多,你可能不會希望使用者開啟網站時一口氣把所有的內容都抓下來。因為這包內容可能很大、需要抓很久,使用者或許也不會探索這個網站的所有內容(抓了也沒用到)。
因此今天就來聊聊「如何根據專案路由來進行分塊」(`・∀・)
設定部分
React 元件
首先在路由層自 react 引用 lazy 與 Suspense 功能,並使用 lazy 來對每一條路由會用到的元件進行懶載入。此時可選擇加上 webpack magic comment 來指定分塊後的名稱以及 prefetch/preload 狀態。
import React, { lazy, Suspense } from "react";
const Product = lazy( () => import( /* webpackChunkName: "product" */ /* webpackMode: "lazy" */ /* webpackPrefetch: true */ "@Component/Page/Product" ),);const Landing = lazy( () => import( /* webpackChunkName: "landing" */ /* webpackMode: "lazy" */ /* webpackPrefetch: true */ /* webpackPreload: true */ "@Component/Page/Landing" ),);const NotFound = lazy( () => import( /* webpackChunkName: "not-found" */ /* webpackMode: "lazy" */ /* webpackPrefetch: true */ "@Component/Page/NotFound" ),);補充:在 webpack 5.87.0 後甚至開始支援 webpackFetchPriority 功能,可指定分塊的載入優先序。
而你的路由元件大致上會長這樣子:
// ...上面是透過懶載入引用的元件
function ReactRouteLayer(): React.ReactElement { /* Main */ return ( <BrowserRouter> <Suspense fallback={<Loading />}> <Switch> <Route path="/" exact> <Landing /> </Route> <Route path="/product" exact> <Product /> </Route> <Route path="/404"> <NotFound /> </Route> <Route path="*"> <Redirect to="/404" /> </Route> </Switch> </Suspense> </BrowserRouter> );}放在 Suspense.fallback 中的 <Loading /> 元件代表「畫面正在載入時,要顯示的替代內容」:
react: Suspense: An alternate UI to render in place of the actual UI if it has not finished loading.
備註:React 的第一版官方文件有比較多關於分塊的說明。
webpack
什麼都不用改。第 13 天介紹的設定內容一個字都不用動,直接執行 make build 來看看加上懶載入的打包結果吧!
成果
使用懶載入:
./build├── static│ ├── css│ │ ├── product-31743c5a.fa288847.chunk.css│ │ ├── landing-15ac0598.766c0cdf.chunk.css│ │ ├── main-31743c5a.8244227e.css│ │ └── not-found.c46f968c.chunk.css│ └── js│ ├── ...其他 60 個分塊│ ├── product-17ac9733.b9ca3a17.js│ ├── product-88d3322f.6af21003.js│ ├── product-d91a9049.6bd193d8.js│ ├── landing-31743c5a.c50d3631.js│ ├── landing-443a0b6e.d0a45c76.js│ ├── landing-e96e9bea.3a9047a7.js│ ├── main-84781932.397ae544.js│ ├── main-c92480b7.d9863f01.js│ ├── main-d91a9049.28df7033.js│ └── not-found.b83ea821.js└── index.html沒有使用懶載入:
./build├── static│ ├── css│ │ └── main-31743c5a.358f495d.css│ └── js│ ├── ...其他 105 個分塊│ ├── main-03bc91d9.778a2136.js│ ├── main-0575efbd.3fe42463.js│ ├── main-17ac9733.9ae86415.js│ ├── main-1c176814.70827397.js│ ├── main-28095760.e2d645d6.js│ ├── main-28c0c43c.4af8e1f2.js│ ├── main-3a8144f2.faf9c54b.js│ ├── main-402b4630.157af26e.js│ ├── main-443a0b6e.dd433636.js│ ├── main-5ad2027e.148cad22.js│ ├── main-6b882012.9a243b76.js│ ├── main-6f34ed7a.4db55c82.js│ ├── main-7ae1fe87.367186f4.js│ ├── main-7c85fd9e.e3c95ef0.js│ ├── main-84781932.9b204099.js│ ├── main-87afd78e.9f1e9bc9.js│ ├── main-a3af9828.92b83948.js│ ├── main-a5409c8c.acca68b3.js│ ├── main-bc4009a1.443e7d0f.js│ ├── main-e0f427da.bcb1dc56.js│ └── main-f16aa68b.cc8b68ba.js└── index.html可以看到 css 與 js 根據路由(landing / product / not-found)進行分塊了。
總結
當 React app 內容越來越多時,可考慮搭配 React 提供的 lazy / Suspense 來執行分塊,避免使用者需要一口氣下載超大包的內容。
如果以上設定有讓你困惑、或是不正確的部分歡迎留言交流 ( ´∀`)