捨棄 create-react-app 之餘還架了個 astro blog 昭告天下:程式碼分塊
除了透過 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
來執行分塊,避免使用者需要一口氣下載超大包的內容。
如果以上設定有讓你困惑、或是不正確的部分歡迎留言交流 ( ´∀`)