使用 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 组件结合使用时处理加载状态。

悬疑

向用户传送大型 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 的代码很小,因此加载旋转图标仅显示很短的时间。较大的组件可能需要更长的时间才能完成加载,尤其是在网络连接较弱时。

为了更好地演示其工作原理,请执行以下操作:

  • 如需预览网站,请按查看应用,然后按全屏 全屏
  • 按 `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>
)

这是一种极为实用的方法,有助于延迟渲染多个组件,同时只显示一种加载状态。提取完所有组件后,用户可以同时看到所有组件显示。

通过下面的嵌入代码,即可看到上述代码:

否则,就很容易遇到交错加载问题,即界面的不同部分接连加载且每个部分都有自己的加载指示器。这可能会让用户感觉很糟糕。

处理加载失败

Suspense 允许您在后台发出网络请求时显示临时加载状态。但如果这些网络请求由于某种原因而失败,该怎么办?您可能处于离线状态,也可能是您的 Web 应用正在尝试延迟加载已过期且在服务器重新部署后不再可用的版本化网址

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. 考虑拆分屏幕外且对用户而言不重要的其他内容。