在前面我們說,過去使用原生 JS 編寫有規模的專案時,因為要模組化,檔案越分越多,最後零散在各處。為了解決這個問題,後來工程師使用打包工具,把所有的 JS 檔綁成一個bundle.js
,在第一次執行網頁時就載入所有程式碼。
但是這也造成了一個問題。
「當專案規模過大時,bundle.js
會很大,導致第一次載入網頁的時間太久。」
這個時候我們就會希望把那些「使用者不會很常進去的頁面」從bundle.js
拿出來。但是要怎麼做呢? 難道還要去webpack.config.js
慢慢設定嗎?
Code-Splitting by React.lazy
在先前,我們要引入 component 檔案時,是使用:
import InputForm from '../component/InputForm';
而 React 提供了一個特殊的引入元件方法lazy()
。React 會把用lazy()
引入的元件在打包時拆成一個獨立的 js 檔案,並且只有在第一次要渲染該元件的時候,才會引入該 js 檔。它的用法是
const 元件 = React.lazy(() => import('檔案相對路徑'));
但是元件載入要一段時間,我們要怎麼處理 lazy 元件還沒被載入的狀況呢?
Suspense
Suspense 是 React 提供的特殊元件,語法如下:
<Suspense fallback={讀取元件}>目標載入元件</Suspense>
當「目標載入元件」還沒載入完成時,React 會顯示fallback
這個 props 綁定的「讀取元件」,一直到「目標載入元件」載入完成後再切換過來。
嗯?這樣我們是不是可以把它拿來處理 ajax 的狀況呢?
的確,React 希望在未來的某一天全面讓大家捨棄在useEffect
呼叫 http request,並且全面改成使用Suspense
。但目前相關的 API 還在實驗開發階段,詳請可以參考並關注官方文件 (opens in a new tab)。
加入 lazy 和 Suspense 到我們的程式碼中
我們來試著在 src/page/FormPage.js 使用 lazy 引入<InputForm />
,並觀察程式碼的狀態:
1. 請先引入 lazy 和 Suspense
import React, { lazy, Suspense } from 'react';
2. 用 lazy 引入 InputForm
const InputForm = lazy(() => import('../component/InputForm'));
3. 用 Suspense 使用 lazy InputForm 元件
import React, { lazy, Suspense } from 'react';
const InputForm = lazy(() => import('../component/InputForm'));
const FormPage = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<InputForm />
</Suspense>
);
};
export default FormPage;
4. 執行npm run build
此時你會發現,build 資料夾出現了一個新的檔案
我們去搜尋本來的bundle.js
,會發現裡面沒有 InputForm 裡面的「提交」按鈕了
而搜尋新的1.bundle.js
,會發現 InputForm 裡面的「提交」按鈕在它裡面。
最後我們查看執行結果,會發現在第一次切換到 FormPage 的時候,React 去 get 了這一隻新的1.bundle.js
。
這樣我們就實現了動態延遲載入元件。