您永远不需要向用户交付超出必要的代码,因此请拆分您的 bundles 以确保这种情况永远不会发生!
通过 React.lazy
方法,可以使用动态导入在组件级别轻松地对 React 应用程序进行代码拆分。
import React, { lazy } from 'react';
const AvatarComponent = lazy(() => import('./AvatarComponent'));
const DetailsComponent = () => (
<div>
<AvatarComponent />
</div>
)
为什么这很有用?
大型 React 应用程序通常由许多组件、实用程序方法和第三方库组成。如果没有努力尝试仅在需要时才加载应用程序的不同部分,则会在用户加载第一个页面后立即向他们交付一个大型 JavaScript bundle。这可能会严重影响页面性能。
React.lazy
函数提供了一种内置方法,可以将应用程序中的组件分隔成单独的 JavaScript 代码块,而无需进行太多准备工作。然后,当您将其与 Suspense
组件结合使用时,可以处理加载状态。
Suspense
向用户交付大型 JavaScript 有效负载的问题在于页面完成加载所需的时间,尤其是在性能较弱的设备和网络连接上。这就是代码拆分和延迟加载非常有用的原因。
但是,当通过网络获取代码拆分的组件时,用户总是会遇到轻微的延迟,因此显示有用的加载状态非常重要。将 React.lazy
与 Suspense
组件结合使用有助于解决此问题。
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 组件作为加载状态。以下示例展示了其工作原理。仅当单击按钮时才会渲染头像,此时会发出请求以检索 suspended AvatarComponent
所需的代码。同时,会显示 fallback 加载组件。
在这里,构成 AvatarComponent
的代码很小,这就是加载微标仅显示很短时间的原因。较大的组件可能需要更长的时间才能加载,尤其是在弱网络连接上。
为了更好地演示其工作原理
- 要预览网站,请按查看应用。然后按全屏
。
- 按 `Control+Shift+J`(在 Mac 上按 `Command+Option+J`)打开 DevTools。
- 点击 Network 选项卡。
- 点击Throttling 下拉菜单,默认设置为 No throttling。选择 Fast 3G。
- 点击应用中的 Click Me 按钮。
现在加载指示器将显示更长时间。请注意,构成 AvatarComponent
的所有代码是如何作为单独的代码块获取的。

暂停多个组件
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
允许您在后台进行网络请求时显示临时加载状态。但是,如果这些网络请求由于某种原因而失败怎么办?您可能处于离线状态,或者您的 Web 应用可能正在尝试延迟加载过期的版本化 URL,并且在服务器重新部署后不再可用。
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 应用程序,请按照以下步骤操作
- 从路由级别开始。路由是识别应用程序中可以拆分的点的最简单方法。React 文档展示了如何将
Suspense
与react-router
一起使用。 - 识别您网站页面上仅在某些用户交互(例如单击按钮)时才渲染的任何大型组件。拆分这些组件将最大限度地减少您的 JavaScript 有效负载。
- 考虑拆分任何屏幕外且对用户不重要的其他内容。