Web 应用中加载指示器快速指南
用户点击了按钮。某些操作正在进行——但具体是什么?如果没有清晰的反馈,他们会往最坏的方向想:应用崩溃了、操作失败了,或者需要再点击一次。加载指示器能解决这个问题,但错误的实现方式会带来新的问题。
本指南涵盖现代加载指示器用户体验设计,从选择正确的模式到实现 React Suspense 加载状态和 Next.js App Router loading.tsx 约定——同时保持 INP Core Web Vitals 指标的健康。
核心要点
- 全局加载动画会阻塞交互,既损害用户体验又影响 INP 分数——应使用局部化、渐进式反馈。
- 根据场景匹配指示器:短暂等待用加载动画,内容加载用骨架屏,可测量操作用进度条,即时感操作用乐观 UI。
- React Suspense 边界和 Next.js
loading.tsx实现了分段作用域的加载状态,让用户可以与应用中未受影响的部分进行交互。 - 可访问性是必须的:使用
aria-busy、实时区域,并尊重减少动画偏好设置。
为什么全局加载动画通常是错误的
全屏加载动画会阻塞所有操作。用户无法阅读内容、导航到其他地方,或做任何有意义的事情。更糟糕的是,即使实际加载时间合理,它们也会损害感知性能。
现代方法:局部化、渐进式反馈。仅在内容实际加载的位置显示加载状态。让用户可以与其他所有内容进行交互。
这对 INP(Interaction to Next Paint,交互到下次绘制)很重要,这是衡量响应性的 Core Web Vital 指标。阻塞交互的全局加载遮罩会对 INP 分数产生负面影响。局部化指示器能保持 UI 其余部分的响应性。
选择正确的指示器
不同场景需要不同的模式:
加载动画(Spinners)
最适合 3 秒以内的短暂、不确定等待。内联使用它们——在按钮内部、表单字段旁边,或小内容区域内。避免在空白屏幕上居中显示加载动画。
骨架屏(Skeleton Screens)
适用于初始页面加载或内容区块。骨架屏显示即将到来内容的形状,减少感知等待时间并防止布局偏移。它们特别适合列表、卡片和文本密集区域。
进度条(Progress Bars)
为确定性流程保留:文件上传、多步骤操作,或任何可以计算实际进度的场景。不反映实际情况的虚假进度条比诚实的加载动画更让用户沮丧。
乐观 UI(Optimistic UI)
对于保存、点赞或切换等操作,立即更新 UI,之后再与服务器协调。用户会感知到即时响应。通过回滚和清晰的错误状态优雅地处理失败。
有效的框架模式
React Suspense 加载状态
React 的 Suspense 边界让你可以声明式地定义加载 UI。用带有 fallback 的 <Suspense> 包裹异步组件,React 会处理其余部分。React 19 通过将异步过渡和待处理 UI 状态作为一等公民,进一步改进了这一点,减少了在状态之间导航时的视觉抖动。
关键洞察:策略性地嵌套 Suspense 边界。单个顶层边界会给你一个全局加载动画——正是你要避免的。多个细粒度边界让不同部分可以独立加载。
Next.js App Router loading.tsx
App Router 的 loading.tsx 约定为每个路由段提供自动加载 UI。在任何路由文件夹中放置一个 loading.tsx 文件,Next.js 会在该段加载时显示它。
关键细节:这是段作用域的,而不是通用的全局加载器。每个路由段可以有自己的加载状态。/dashboard 中的 loading.tsx 只影响仪表板段,不影响整个应用外壳。
这与流式 SSR 自然配合——内容随着数据可用而逐步渲染,loading.tsx 填补空白。
Discover how at OpenReplay.com.
View Transitions API
对于页面和路由变化,View Transitions API 越来越成为传统加载动画的可行替代方案。浏览器可以在状态之间平滑动画过渡,而不是在准备下一页时显示加载器。即使实际加载时间相似,这种方式感觉更快。
该 API 跨框架工作,并提供 CSS 钩子来自定义过渡动画。它对于你控制两个页面的同源导航特别有效。
可访问性要求
加载指示器必须对所有人有效:
aria-busy="true":应用于内容正在加载的容器。屏幕阅读器会宣布忙碌状态。role="status"配合实时区域:用于应该被宣布的加载消息。使用aria-live="polite"避免打断用户。- 减少动画:尊重
prefers-reduced-motion。用静态指示器或细微的不透明度变化替换旋转动画。 - 永远不要仅依赖视觉:将动画指示器与文本标签或 ARIA 宣布配对。“正在加载您的仪表板…”胜过无声的加载动画。
常见错误
为快速操作显示加载器:如果某项操作在 100 毫秒内完成,加载指示器会产生闪烁。对加载状态进行防抖——仅在短暂延迟后显示它们。
不必要地阻塞交互性:除非操作确实需要等待(如支付确认),否则让用户继续使用应用。
误导性进度条:卡在 99% 两分钟的进度条会摧毁信任。如果无法测量真实进度,请使用不确定指示器。
隐藏已加载的内容:刷新数据时,显示带有细微刷新指示器的旧内容,而不是用骨架屏替换所有内容。
结论
良好的加载指示器用户体验归结为诚实和局部性。告诉用户正在发生什么、在哪里发生,并让他们做其他所有事情。React Suspense 加载状态、Next.js App Router loading.tsx 和 View Transitions API 等现代模式使这比以往更容易——同时保持 INP Core Web Vitals 指标的健康。
从审核当前的加载状态开始。用局部化反馈替换全局阻塞器。你的用户和性能指标都会感谢你。
常见问题
在加载具有可预测布局的内容时使用骨架屏,例如列表、卡片或文本块。骨架屏通过显示即将到来内容的形状来减少感知等待时间。加载动画更适合短暂、不可预测的等待或按钮和表单字段内的内联反馈。
在显示加载指示器之前添加短暂延迟,通常为 100 到 200 毫秒。如果操作在延迟到期前完成,则完全跳过指示器。这可以防止刺眼的闪烁,同时仍为真正缓慢的操作提供反馈。
不会。loading.tsx 文件是段作用域的,意味着它只影响放置它的路由段。dashboard 文件夹中的 loading.tsx 仅在该段加载时显示。应用外壳的其余部分保持交互性且不受影响。
对包含加载内容的容器应用 aria-busy true。对应该被宣布的加载消息使用 role status 配合 aria-live polite。始终将视觉指示器与文本标签或 ARIA 宣布配对,以便看不到动画的用户仍能收到反馈。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.