這一篇要來介紹 React Router 中很重要一個優化的部分,也就是 Lazy Loading,Lazy Loading 在實在開發上其實是非常重要的,它可以將我們的程式碼分割成不同的 chunk,然後在你需要或請求的時候才去載入相對應檔案,所以這一篇就會來介紹 Lazy Loading 的部分。
這邊我們將會使用這一份範例程式碼當作示範。
當你下載了前面的範例程式碼後,接著你可以在終端機輸入 npm run build
來看一下打包狀況,你會發現打包出來的檔案會是這樣的:
> vite-project@0.0.0 build
> vite build
vite v3.1.4 building for production...
✓ 39 modules transformed.
dist/index.html 0.44 KiB
dist/assets/index.f9c6795a.css 4.70 KiB / gzip: 1.53 KiB
dist/assets/index.50715e86.js 157.74 KiB / gzip: 51.15 KiB
我們可以看到上方中有一個 dist/assets/index.50715e86.js
,這個檔案就是我們的整個專案打包編譯後的程式碼,而這個檔案大小為 157.74 KiB,雖然看起來好像沒有很大,但是如果我們的專案越來越龐大時,這個檔案也會跟著越來越大,那麼就會導致一些問題發生,其中最明顯的就是使用者在瀏覽網頁時,會因為網頁的載入時間太長而導致使用者離開,所以這時候就需要 Lazy Loading 來幫助我們。
當你使用了 Lazy Loading 之後,你會發現打包出來的檔案會類似變成這樣子
> vite-project@0.0.0 build
> vite build
vite v3.1.4 building for production...
✓ 40 modules transformed.
dist/index.html 0.44 KiB
dist/assets/Products.6a1dfb49.js 0.14 KiB / gzip: 0.14 KiB
dist/assets/App.2df53b30.js 0.11 KiB / gzip: 0.12 KiB
dist/assets/ToDoList.1f52c4c4.js 0.13 KiB / gzip: 0.13 KiB
dist/assets/Admin.868445da.js 0.13 KiB / gzip: 0.13 KiB
dist/assets/index.f9c6795a.css 4.70 KiB / gzip: 1.53 KiB
dist/assets/index.df8ef9bc.js 158.30 KiB / gzip: 51.47 KiB
我們可以看到整體檔案縮小了相當多,透過 Lazy Loading 我們可以將我們的程式碼分割成不同的 chunk,然後在你需要或請求的時候才去載入相對應檔案,所以這邊我們就來看一下怎麼使用 Lazy Loading 吧。
首先打開放在 routes 資料夾中的 index.js
,接著我們可以看到這樣的程式碼
import { useRoutes } from 'react-router-dom';
import App from '../App';
import ToDoList from '../ToDoList';
import Products from '../Products';
import Admin from '../Admin';
const routes = [
{
path: '/',
element: <App />,
},
{
path: '/todolist',
element: <ToDoList />,
},
{
path: '/products',
element: <Products />,
},
{
path: '/admin',
element: <Admin />,
}
];
export default () => useRoutes(routes);
接著我們要在最上方引入 lazy
這個函式
import { lazy } from 'react';
那麼 lazy
是什麼呢?簡單來說就是一個函式,這個函式可以讓我們在需要的時候才去載入相對應的程式碼,而不是一開始就載入所有程式碼,所以我們可以將 App
、ToDoList
、Products
、Admin
這四個元件都改成使用 lazy
來載入
import { lazy } from 'react';
const App = lazy(() => import('../App'));
const ToDoList = lazy(() => import('../ToDoList'));
const Products = lazy(() => import('../Products'));
const Admin = lazy(() => import('../Admin'));
接下來我們要在 App.js
中引入 Suspense
這個元件,這個元件可以讓我們在載入元件時顯示一些 Loading 的畫面,基本使用方式很簡單
<Suspense fallback={ Loading 元件 }>即將載入的元件<Suspense>
那為什麼要使用 Suspense
呢?主要原因是因為元件是使用 lazy
來載入的,所以在載入元件時會有一些延遲,所以我們可以使用 Suspense
來顯示一些 Loading 的畫面,讓使用者知道正在載入中,而不是一直等待載入完成,所以我們可以在 App.js
中加入 Suspense
來顯示 Loading 畫面
import { StrictMode, Suspense } from 'react'
import { HashRouter, NavLink } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import Router from './routes'
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<StrictMode>
<HashRouter>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
{/* ...略過 */}
</nav>
<Suspense fallback={<div>Loading...</div>}>
<Router />
</Suspense>
</HashRouter>
</StrictMode>
)
Suspense
的 fallback
這個屬性是可以接收一個元件的,所以上方我們放入了一個 div
,裡面放入 Loading...
的文字,這樣當我們載入元件時就會顯示 Loading...
的文字,而不是一直等待載入完成。
但是這邊要注意,若你沒有使用 Suspense
的話,是會出現以下的錯誤訊息
Uncaught Error: A component suspended while responding to synchronous input. This will cause the UI to be replaced with a loading indicator. To fix, updates that suspend should be wrapped with startTransition.
因此我們必須要使用 Suspense
來顯示 Loading 畫面,這樣才不會出現錯誤訊息。
那麼剛剛有提到 fallback
也可以寫入一個元件來使用,所以這邊就簡單示範一下,首先先建立一個 components/Loading.js
來放入一個 Loading 的元件
// components/Loading.jsx
const Loading = () => {
return (
<div className="flex justify-center items-center h-screen bg-indigo-500">
<img src="https://i.imgur.com/0HSGxif.gif" alt="" className="w-[150px] h-[150px] "/>
</div>
)
}
export default Loading;
接下來在 main.jsx 引入並將 Suspense
的 fallback
替換成 Loading
元件
import { StrictMode, Suspense } from 'react'
import { HashRouter, NavLink } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import Router from './routes'
import Loading from './components/Loading';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<StrictMode>
<HashRouter>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
{/* ...略過 */}
</nav>
<Suspense fallback={ <Loading/> }>
<Router />
</Suspense>
</HashRouter>
</StrictMode>
)
接下來只要是初次載入的元件畫面都會出現 Loading 的畫面,而當你第二次載入時,就不會再出現 Loading 的畫面了。
那麼這一篇就先到這邊結束囉~
範例程式碼一樣上傳到這邊。
本文將會同步更新到我的部落格