
最小用法
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 会让组件一直重新挂起。这一层缓存通常由框架提供,业务代码里别自己现造。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 189 天
部分内容可能已过时