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号