使用 React.lazy 和 Suspense 分割程式碼

您不需要向使用者提供比必要更多的程式碼,因此請分割套件,確保不會發生這種情況!

React.lazy 方法可讓您輕鬆使用動態匯入作業,在元件層級上以程式碼分割 React 應用程式。

import React, { lazy } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const DetailsComponent = () => (
  <div>
    <AvatarComponent />
  </div>
)

這種報表有哪些優點?

大型 React 應用程式通常包含許多元件、公用程式方法和第三方程式庫。如果您沒有嘗試在需要時才載入應用程式的不同部分,使用者載入第一個網頁時,就會收到單一大型 JavaScript 套件。這可能會大幅影響網頁效能。

React.lazy 函式提供內建方式,可將應用程式中的元件分割為個別的 JavaScript 區塊,且不需要太多額外工作。接著,您可以將其與 Suspense 元件配對,處理載入狀態。

Suspense (懸疑)

向使用者傳送大量 JavaScript 酬載的問題,是網頁完成載入所需的時間長度,特別是在效能較差的裝置和網路連線中。因此,程式碼分割和延遲載入非常實用。

不過,當使用者透過網路擷取程式碼分割元件時,系統一律會出現輕微延遲,因此請務必顯示實用的載入狀態。將 React.lazySuspense 元件搭配使用,有助於解決這個問題。

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
  </Suspense>
)

Suspense 會接受 fallback 元件,讓您將任何 React 元件顯示為載入狀態。以下範例說明這項機制的運作方式。只有在按下按鈕時才會顯示顯示圖片,並且會提出要求,以便擷取暫停的 AvatarComponent 所需的程式碼。在此同時,系統會顯示備用載入元件。

在這裡,構成 AvatarComponent 的程式碼很小,因此載入旋轉圖示只會顯示一段短時間。較大的元件可能需要更長的時間才能載入,尤其是在網路連線不佳的情況下。

為進一步說明這項功能的運作方式:

  • 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕
  • 按下 `Control + Shift + J` 鍵 (在 Mac 上為 `Command + Option + J` 鍵) 開啟開發人員工具。
  • 按一下 [網路] 分頁標籤。
  • 點選「Throttling」下拉式選單,預設值為「No throttling」。選取「快速 3G」
  • 按一下應用程式中的「Click Me」按鈕。

載入指標現在會顯示更長的時間。請注意,所有組成 AvatarComponent 的程式碼都會以個別區塊擷取。

開發人員工具的網路面板,顯示正在下載的 chunk.js 檔案

暫停多個元件

Suspense 的另一項功能是,您可以暫停多個元件的載入作業,即使這些元件都是延遲載入

例如:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

const DetailsComponent = () => (
  <Suspense fallback={renderLoader()}>
    <AvatarComponent />
    <InfoComponent />
    <MoreInfoComponent />
  </Suspense>
)

這是延遲多個元件算繪作業,同時只顯示單一載入狀態的實用方法。所有元件擷取完成後,使用者就能看到所有元件同時顯示。

您可以透過下列嵌入內容查看:

否則,您很容易遇到不同步載入問題,或 UI 的不同部分會依序載入,且每個部分都有各自的載入指標。這可能會讓使用者體驗變得更加突兀。

處理載入失敗

Suspense 可讓您在網路要求發生時,顯示暫時的載入狀態。但如果這些網路要求因某些原因失敗,該怎麼辦?您可能處於離線狀態,或是您的網路應用程式嘗試以延遲載入方式載入已過時的版本網址,且在伺服器重新部署後,該網址已無法使用。

React 提供標準模式,可妥善處理這類載入失敗情形:使用錯誤邊界。如說明文件所述,如果 React 元件實作生命週期方法 static getDerivedStateFromError()componentDidCatch() (或兩者皆實作),便可做為錯誤邊界。

如要偵測及處理延遲載入失敗情形,您可以使用做為錯誤邊界的父項元件,將 Suspense 元件包裝起來。在錯誤邊界 render() 方法內,如果沒有錯誤,您可以原樣轉譯子項,如果發生錯誤,則轉譯自訂錯誤訊息:

import React, { lazy, Suspense } from 'react';

const AvatarComponent = lazy(() => import('./AvatarComponent'));
const InfoComponent = lazy(() => import('./InfoComponent'));
const MoreInfoComponent = lazy(() => import('./MoreInfoComponent'));

const renderLoader = () => <p>Loading</p>;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hasError: false};
  }

  static getDerivedStateFromError(error) {
    return {hasError: true};
  }

  render() {
    if (this.state.hasError) {
      return <p>Loading failed! Please reload.</p>;
    }

    return this.props.children;
  }
}

const DetailsComponent = () => (
  <ErrorBoundary>
    <Suspense fallback={renderLoader()}>
      <AvatarComponent />
      <InfoComponent />
      <MoreInfoComponent />
    </Suspense>
  </ErrorBoundary>
)

結論

如果不確定要從何處開始在 React 應用程式中套用程式碼分割,請按照下列步驟操作:

  1. 從路線層級開始。路由是識別應用程式可分割點的最簡單方法。React 文件說明如何搭配使用 Suspensereact-router
  2. 找出網站頁面中任何大型元件,這些元件只會在特定使用者互動 (例如點選按鈕) 時顯示。分割這些元件可盡可能減少 JavaScript 酬載。
  3. 請考慮將畫面外且對使用者不重要的任何內容分割。