阅读时间约 11 分钟。
我们开发的 React 应用随着规模越来越大,如果还是将所有组件打包到一个 bundle 文件中,就可能会导致应用程序的加载时间变慢。常见的做法是,将程序中的组件打包到单独的文件中以减少应用的加载时间,通常我们会通过“按需加载”的方式来实现这一目的。在本文中,你将学会在 React 程序中关于组件动态导入的相关知识。
1. 什么是动态导入组件?
动态导入组件是指在需要时才加载组件,而不是在应用程序加载时将所有组件都打包到一个 bundle 文件中。在 React 中,我们该如何实现呢?
React 为我们提供了一个 lazy()
方法和 Suspense
组件,这是实现动态导入组件的两个主要工具。
使用 React.lazy()
,我们可以轻松地实现按需加载组件,它的语法如下:
const MyComponent = React.lazy(() => import('./MyComponent'));
在上面的代码中,React.lazy()
接收一个函数,该函数返回一个 import()
函数调用。import()
函数是 ECMAScript 动态导入语法的一部分,它允许我们在运行时异步加载一个模块。通过将 React.lazy()
和 import()
函数结合使用,我们便可以按需加载组件。
当我们使用 React.lazy()
时,React 会自动将返回的组件包装在一个 lazy
组件中。因此,我们需要使用 Suspense
组件来渲染这个 lazy
组件。Suspense
组件允许我们在组件加载完成之前显示一个 loading 界面。下面是一个使用 React.lazy()
和 Suspense
组件的简单例子:
import React, { lazy, Suspense } from 'react';
const MyComponent = lazy(() => import('./MyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
</div>
);
}
在上面的代码中,当 <MyComponent />
组件被渲染时,React 将自动异步加载 MyComponent
模块。fallback
属性指定了当组件加载时显示的 loading 界面。当组件加载完成时,React 将自动将其呈现。
2. 异步列表组件示例
下面我们通过一个「异步列表」组件示例来演示 React.lazy() 和 Suspense,先看最终的效果:
2.1 父组件 ArtistPage
我们通过一个父组件 ArtistPage 和一个子组件 Albums 来实现上面 gif 中的效果。父组件 ArtistPage 实现思路如下:
- 页面加载完成后,默认渲染「Open The Beatles artist page」按钮,如果按钮被点击则展示列表数据;
- 在展示列表数据前,首先通过 Suspense 展示一个过渡用的 Loading 页面,当子组件和其中的数据请求完成后再展示列表数据。
父组件 ArtistPage 代码如下:
import { Suspense, useState } from "react";
import Albums from "@/components/Albums";
export default function ArtistPage() {
const [show, setShow] = useState(false);
// 定义组件的状态 show,控制组件的渲染
// 初始时设置为 false,点击按钮后设置为 true
// 以展示 The Beatles 的专辑页面
if (show) {
// 如果 show 为 true,则渲染以下内容
return (
<>
<h1>The Beatles</h1>
{/* 使用 React Suspense 包装组件,添加 fallback UI */}
<Suspense fallback={<Loading />}>
{/* 传入 artistId 属性,渲染 The Beatles 的专辑 */}
<Albums artistId="the-beatles" />
</Suspense>
</>
);
} else {
// 如果 show 为 false,则渲染以下按钮
return (
<button onClick={() => setShow(true)}>
Open The Beatles artist page
</button>
);
}
}
// 定义 Loading 组件,用于在数据加载时显示
function Loading() {
return <h2>