iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
Modern Web

終究都要學 React 何不現在學呢?系列 第 28

終究都要學 React 何不現在學呢? - React Router - Lazy Loading - (28)

  • 分享至 

  • xImage
  •  

前言

這一篇要來介紹 React Router 中很重要一個優化的部分,也就是 Lazy Loading,Lazy Loading 在實在開發上其實是非常重要的,它可以將我們的程式碼分割成不同的 chunk,然後在你需要或請求的時候才去載入相對應檔案,所以這一篇就會來介紹 Lazy Loading 的部分。

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 是什麼呢?簡單來說就是一個函式,這個函式可以讓我們在需要的時候才去載入相對應的程式碼,而不是一開始就載入所有程式碼,所以我們可以將 AppToDoListProductsAdmin 這四個元件都改成使用 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>
)

Suspensefallback 這個屬性是可以接收一個元件的,所以上方我們放入了一個 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 引入並將 Suspensefallback 替換成 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 的畫面了。

那麼這一篇就先到這邊結束囉~

範例程式碼一樣上傳到這邊

後記

本文將會同步更新到我的部落格


上一篇
終究都要學 React 何不現在學呢? - React Router - Hook - (27)
下一篇
終究都要學 React 何不現在學呢? - React Redux - Redux 與快速入門 - (29)
系列文
終究都要學 React 何不現在學呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言