Posted by Socrates on | Featured
龐大的JavaScript代碼包會降低應用程序的運行速度。當大量代碼同時被加載時,用戶需要等待更長時間才能看到頁面內容,而且頁面的反應速度也會變慢。搜索引擎也可能會將這類網站的排名降得較低。
懶加載技術通過將代碼分割成較小的部分,并僅在真正需要時才進行加載,從而幫助解決這一問題。
本指南將為您詳細介紹如何在React和Next.js中使用懶加載功能。閱讀完本指南后,您將了解何時該使用`React.lazy`、`next/dynamic`以及`Suspense`,同時還會獲得一些可供您復制并應用于自己項目的實際示例。
目錄
什么是懶加載?
懶加載是一種性能優化技術,它會在代碼真正被需要時才進行加載。與其一次性加載整個應用程序,不如將其分割成較小的部分——只有當用戶訪問某個特定頁面或使用某項功能時,瀏覽器才會下載相應的代碼片段。
懶加載帶來的好處包括:
-
更快的初始加載速度:由于代碼被分成了小塊,因此應用程序的啟動速度會更快。
-
更好的核心Web性能指標:懶加載有助于提升“最大內容繪制時間”和“總阻塞時間”等關鍵指標。
-
更低的帶寬消耗:用戶只需下載實際需要的代碼,從而節省了網絡資源。
在React中,您可以通過動態導入功能以及`React.lazy()`來實現懶加載;而在Next.js中,則可以使用`next/dynamic`來完成同樣的任務。
先決條件
在開始學習之前,請確保您已經具備以下條件:
-
對React有基本的了解,包括組件、鉤子以及狀態管理等概念。
-
已安裝Node.js(建議使用18版或更高版本)。
-
擁有一個React應用程序(可以使用Create React App或Vite創建),或者一個Next.js應用程序(用于后續的示例學習)。
對于React相關的示例,您可以選擇使用Create React App或Vite;而對于Next.js的示例,則需要使用App Router功能(Next.js 13版及以上版本支持)。
如何使用`React.lazy`進行代碼分割
React.lazy()允許你將某個組件定義為動態導入的組件。React只會在該組件首次被渲染時才會加載它。React.lazy() 需要一個能夠返回動態調用的 import() 函數。被導入的模塊必須使用默認導出方式。
下面是一個基本的示例:
import { lazy } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const AdminDashboard = lazy(() => import('./Admin Dashboard');
function App() {
return (
);
}
如果使用帶名稱的導出,你可以將它們映射為默認導出:
const ComponentWithNamedExport = lazy(() => {
import('./MyComponent').then((module) => ({
default: module NamedComponent,
}));
});
你還可以為這些代碼塊指定名稱,以便在瀏覽器中進行更便捷的調試:
const HeavyChart = lazy(() => {
import(/* webpackChunkName: "heavy-chart" */ './HeavyChart');
});
單憑 React.lazy() 是不夠的。你必須將這些懶加載組件包裹在 Suspense 中,這樣 React 才知道在它們加載期間應該顯示什么內容。
如何將 Suspense 與 React.lazy 一起使用
Suspense 是一個 React 組件,當它的子組件正在加載時,它會顯示替代內容。它與 React.lazy() 結合使用,可以處理動態導入組件的加載狀態。
將你的懶加載組件包裹在 Suspense 中,并提供一個 fallback 屬性:
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const AdminDashboard = lazy(() => import('./Admin Dashboard');
function App() {
return (
我的應用
正在加載圖表...
正在加載管理面板...
);
}
你也可以為多個懶加載組件使用同一個 Suspense 結構:
正在加載中...
一個設計得更精良的替代內容顯示方式,能夠提升用戶體驗:
function LoadingSpinner() {
return (
);
}
}>
如何使用錯誤邊界來處理錯誤
React.lazy() 和 Suspense 并不能處理加載錯誤(例如網絡故障或某些代碼塊缺失)。為此,你需要使用錯誤邊界。
“錯誤邊界”是一種組件,它使用`componentDidCatch`或`static getDerivedStateFromError`來捕獲其子樹中出現的錯誤,并顯示替代界面。
下面是一個簡單的錯誤邊界示例:
import { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('錯誤被錯誤邊界捕獲:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || 發生了錯誤。
;
}
return this.props.children;
}
}
請將你的`Suspense`組件包裹在錯誤邊界中:
import { lazy, Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary';
const HeavyChart = lazy(() => import('./HeavyChart');
function App() {
return (
圖表加載失敗,請重試。
正在加載圖表……
);
}
如果某個組件無法成功加載,錯誤邊界會捕獲這個錯誤,并顯示替代界面,而不會讓頁面顯示空白內容或出現未處理的錯誤。
如何在Next.js中使用`next/dynamic`
Next.js提供了`next/dynamic`這個功能,它結合了`React.lazy()`和`Suspense`,并為Next.js添加了一些專門的設計選項(包括服務器端渲染功能)。
基本用法如下:
'use client';
import dynamic from 'next/dynamic';
const ComponentA = dynamic(() => import('../components/A'));
const ComponentB = dynamic(() => import('../components/B');
export default function Page() {
return (
);
}
自定義加載界面
你可以通過設置`loading`選項,在組件加載期間顯示占位符:
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
loading: () => 正在加載圖表……
,
});
禁用服務器端渲染
對于那些只能在客戶端運行的組件(例如那些使用`window`或僅瀏覽器可用API的組件),請將`ssr: false`設置為這些組件的屬性:
const ClientOnlyMap = dynamic(() => import('../components/Map'), {
ssr: false,
loading: () => 正在加載地圖……
,
});
注意:`ssr: false`僅適用于客戶端組件。請在包含`’use client’`語句的文件中使用這個選項。
按需加載組件
只有當滿足特定條件時,你才能加載某個組件:
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('../components/Modal'), {
loading: () => 正在加載模態框...
,
});
export default function Page() {
const [showModal, setShowModal] = useState(false);
return (
{showModal && }
);
}
命名導出
對于需要命名導出的情況,你需要通過動態導入來獲取相應的組件:
const Hello = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
);
結合 next/dynamic 使用 Suspense
在 React 18 及更高版本中,你可以使用 suspense: true,這樣就可以依賴父組件的 Suspense 結構來處理加載邏輯,而無需使用 loading 選項:
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
suspense: true,
});
// 在你的組件中:
正在加載中...