React Lazy和Suspense
在React中,有些組件需要按需加載,比如一些協議的彈窗,因為幾乎沒人看。React.lazy()接受一個函數,返回一個組件,這個組件就會按需加載。函數的格式是() => import(要引入組件所在的js文件),js文件必須用export default 暴露出組件。假設Model.js中 export default function Module() {},
const LazyModel = React.Lazy(() => import('./Model.js'));
LazyModel 初次渲染時,才會加載Model.js的代碼。加載需要時間,最好給個提示,正在加載中。用Suspense組件把動態渲染的組件包起來,同時給Suspense組件一個fallback。當React遇到 Suspense組件時,它會檢查是否任何子組件正在等待 Promise 的 resolve。如果是,React 會“暫停”這些組件的渲染,并顯示一個后備UI,從而繼續渲染元素樹。當Promise被resolve,真正的組件替換到后備UI。
<Suspense fallback={<div>Loading...</div>}>
<CalendarWrapper />
</Suspense>
怎么實現按需加載的?把上面的代碼拆分一下,
const getPromise = () => import('./Model.js'); // import()函數返回的是promise, promise resolve后返回的是module對象(Model.js中暴露出來的對象),
const LazyComponent = lazy(getPromise);
我們可以認為動態加載的組件有一個內部狀態,uninitialized, pending, resolved或rejected。當React首次渲染動態加載的組件時,組件是uninitialized狀態,React會調用函數() => import('./Model.js')來加載組件。函數返回了一個promise,組件的狀態就變成了pending 狀態,等待promise完成。promise應該能resolve成一個模塊,模塊的default屬性是一個component。一旦promise被resolve,React就會設置組件的狀態是resolved,然后返回組件,表示可以渲染了。
if (status === "resolved") {
return component;
} else {
throw promise;
}
上面的else就是和suspense組件進行溝通的關鍵,如果promise 沒有resolve呢?它就會拋出promise, suspense組件就是捕獲promise,然后渲染fallback UI(如果 promsise的狀態是pending)。 所以對一個動態渲染的組件來說,如果它已經包含了一個組件,就是組件已經resolve了,React就會直接渲染組件;如果它包含一個正在pending的promise,組件就會拋出promise,Suspense組件就會捕獲它。如果它包含一個函數,它就會調用函數來獲取promise,把promise存在組件對象上,拋出promise,等到promise resolve后,調用promise的then方法 把promise resolve的組件存儲在組件對象上。
<aside>
<Suspense fallback={<div>Loading...</div>}>
<CalendarWrapper />
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<CalendarWrapper />
</Suspense>
</aside>
使用fallback屬性來表示Suspense組件將要渲染什么,直到所有后代Lazy組件都加載完成,返回UI。異步加載的組件如果已經加載了,它就不需要再加載了。所以這兩個異步加載的組件也可以放到一個<Suspense> 下面
<Suspense fallback={<div>Loading...</div>}>
<CalendarWrapper />
<CalendarWrapper />
</Suspense>
當一個lazy組件第一次渲染時,React會沿著組件樹向上查找Suspense組件,然后使用第一個找到的Suspense組件。Suspense組件就會在它子組件的地方渲染它的fallback UI,如果沒有找到Suspense組件,React就會報錯。
如果promsise 被rejected,比如網絡錯誤,suspense component 不會處理error,需要error boundary。react并沒有提供一個組件來捕獲子組件拋出來的錯誤,但是它提供了一系列的生命周期函數,如果在類組件中想要捕獲錯誤,就要實現這些生命周期函數,如果一個類實現一個或幾個這樣的生命周期函數,它們就稱為錯誤邊界。如果你把組件包到錯誤邊界中,如果被包裹的組件中拋出錯誤,它就是渲染fallback UI。
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<CalendarWrapper />
<CalendarWrapper />
</Suspense>
</ErrorBoundary>
Error buond 組件
import { Component } from "react";
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
const {
children,
fallback = <h1>Something went wrong.</h1>
} = this.props;
return this.state.hasError ? fallback : children;
}
}
當React去渲染動態加載的組件時,它先判斷組件的狀態,如果動態引入的組件已經加載完了,直接渲染組件。如果組件還在pending狀態,React就會拋出動態引入的promise,如查promise rejected,需要一個error bound 來捕獲異常,并渲染fallback UI。

浙公網安備 33010602011771號