R37第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

Suspense 与加载体验

Suspense 与加载体验 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完你能解释 Suspense 边界的工作方式:边界内的子组件在数据未就绪时抛出 Promise,最近的边界接管并显示 fallback;能按用户的等待粒度合理放置边界,让加载只发生在局部,并用 startTransition 避免切换时的整页闪烁。

问题场景

课程详情页:顶部标题、中间章节列表、右侧相关推荐。第一版把加载逻辑写成「数据没到就整页返回加载动画」,于是切个数据源,整页白一下、骨架屏闪一下,用户刚看到的内容全部消失。加载体验差,往往不是没有 loading,而是 loading 的边界放错了位置。

核心解释

Suspense 的机制一句话:子组件在渲染中抛出一个 Promise(通常由数据层封装),React 捕获后找到最近的 <Suspense> 显示 fallback;Promise 决议后自动重试渲染并展示真实内容。

<Suspense fallback={<ChaptersSkeleton />}>
  <Chapters id={id} /> {/* 抛出 Promise 时,只有这里被 fallback 替换 */}
</Suspense>

边界放置有两条经验。第一,fallback 的粒度 = 用户的等待粒度:用户切一个数据源时期待的是「对应区域内容刷新」,整页 fallback 就过重;首屏期待的是「页面骨架」,单个小卡片的 fallback 又太碎。第二,已经被看到的内容不要因为别处的加载而消失——导航类更新包进 startTransition,新数据未就绪时 React 会保留旧界面:

function onSelect(nextId) {
  startTransition(() => setSourceId(nextId)); // 旧内容保留到新内容就绪
}

什么情况用:组件树里有异步数据或懒加载代码、希望「声明式地」描述加载态时,用 Suspense 组织;如果只是「请求期间显示 spinner」,局部 state 加条件渲染一样能做,不必强行套 Suspense。验证方式:在实验区把边界从「整页一个」改成「每个面板各一个」,切换数据时对比:前者整页闪骨架,后者只有对应面板变化。

示例拆解

实验区模拟课程详情页的两个面板(章节列表 / 相关推荐),数据各有 900ms 延迟:

  • 「局部边界」开关:开 = 每个面板各自包 Suspense;关 = 整页一个 Suspense;
  • 「刷新两个面板的数据」按钮触发新一轮请求;整页模式下任一面板加载都会整页骨架闪现;
  • 「切换包进 startTransition」开关:开启后切换时旧内容保留,按钮上出现「加载中(旧内容保留)」,新内容就绪后一并切换;
  • 验证:三种配置各切换两次,对比闪烁范围、内容保留情况和 isPending 提示。

动手任务

打开本课实验区:

  1. 关掉「局部边界」刷新两次,记录整页闪烁的体验;再打开局部边界对比,说出差别。
  2. 给面板容器固定最小高度(现在是 90px),让局部加载的骨架不引起布局跳动。
  3. 打开 startTransition 后连续快速切换两次,确认最终显示的是最后一次数据——说明被打断的过渡不会留下中间结果。

常见错误

  • 边界放得太高:整页一个 Suspense,任何局部加载都整页闪烁;fallback 粒度要跟随用户的等待粒度。
  • 抛出 Promise 却没有边界接:组件在渲染中抛出 Promise 而上层没有 Suspense,React 直接报错;抛出必须配对边界。
  • 把 Suspense 当错误处理:它只管「未就绪」,请求失败抛出的是错误,要靠下一课的错误边界接管。
  • fallback 与真实内容尺寸差太多:加载完成瞬间布局大跳;给 fallback 预留接近真实内容的占位高度。

检查与复盘

自查清单:

  • 能完整复述机制:抛 Promise → 最近边界显示 fallback → 就绪后重试渲染
  • 边界粒度经过设计,页面不会因为局部加载而整页闪
  • 导航类切换包了 startTransition,旧内容保留、验证过不闪

参考资料

动手实验
动手实验
预览
点击「运行」在隔离沙箱中预览
检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选简单

关于 <Suspense fallback={<Spinner />}>,下列说法正确的是?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...