599 字
3 分钟
React 的 lazy 与 Suspense

最小用法#

import { lazy, Suspense } from 'react'
const DataChart = lazy(() => import('./DataChart'))
function Page() {
return (
<Suspense fallback={<p>图表加载中……</p>}>
<DataChart />
</Suspense>
)
}

lazy 接收一个无参函数,它返回 Promise;Promise 解析出的模块,其默认导出就是组件。Suspense 在它加载期间渲染 fallback,加载完再换成真组件。

一个 Suspense 可以包多个懒组件,共用一个 fallback。嵌套时内层的 fallback 先顶上,它先加载完就先显示。

加载失败要靠错误边界接住#

断网、文件 404 时 lazy 会抛错。这个错误由最近的错误边界处理;没有边界,整棵树会被打崩。所以懒加载的组件外面基本都要有一个边界:

<ErrorBoundary fallback={<p>图表加载失败</p>}>
<Suspense fallback={<p>图表加载中……</p>}>
<DataChart />
</Suspense>
</ErrorBoundary>

错误边界得用类组件写(getDerivedStateFromError),这是它至今仍是类组件的原因之一。

配合过渡就不闪加载态#

切换标签页时如果新内容要等,默认会先显示 fallback,视觉上闪一下。把那次状态更新包进 startTransition,已经显示的内容会保留到新内容就绪(见 useTransition)。

服务端渲染能用#

旧教程里常见一句「lazy 只支持客户端渲染,SSR 要用 loadable-components 或 next/dynamic」。那是 React 18 之前的结论,已经不成立:lazy 与 Suspense 现在在服务端渲染下可用,而且是流式渲染的基础,服务端可以先把 fallback 发出去,内容就绪后再补上。

按路由切,别按组件切#

把首屏必须的按钮、输入框也拆出去,只会多几次请求,首屏反而更慢。按路由或按业务模块切才有收益。

给代码块起名字这类配置属于打包器:webpack 用魔法注释,Vite 与 Rollup 有各自的方式,写法不通用,抄之前先确认自己的构建工具。

数据也能触发 Suspense#

除了懒加载,读取 Promise 也会让组件挂起。官方给的 API 是 use:

import { use } from 'react'
function Comments({ commentsPromise }) {
const comments = use(commentsPromise)
return (
<ul>
{comments.map(c => <li key={c.id}>{c.text}</li>)}
</ul>
)
}

两条限制要注意:use 不能包在 try/catch 里;传进去的 Promise 必须来自缓存,每次渲染新建一个 Promise 会让组件一直重新挂起。这一层缓存通常由框架提供,业务代码里别自己现造。

第 10 篇,共 39 篇本文属于系列

React 的 lazy 与 Suspense
https://www.choria.top/posts/suspense/
作者
乔瑞雅·克林威尔
发布于
2026-05-03
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 189 天

部分内容可能已过时