设计原则
加载反馈的目标不是让等待更花哨,而是让用户知道系统是否响应、哪里正在变化、是否还能行动、等待是否可估计、失败后如何恢复。不同等待场景需要不同的反馈模式。
大理论
加载状态是 visibility of system status 的前端契约。0.1 秒内的直接操控应几乎立即反馈;约 1 秒内的导航和查询不应闪出骨架屏;几秒级的整页或列表加载需要让用户理解最终结构;超过 10 秒或不可预测的任务需要明确进度、阶段、取消、后台处理或通知路径。Skeleton、spinner、progress bar、inline loading 和 optimistic UI 不是风格选择,而是由时长、范围、可测量性和风险共同决定。
小知识点
- Skeleton 适合首屏、整页、表格、卡片列表和详情页初始加载,前提是它能模拟最终信息架构,而不是只显示 app frame。
- Inline loading 适合保存、刷新、创建、删除、表格行状态和小区域更新;它应该出现在被替换内容或触发按钮原位。
- Spinner 只能表达系统仍在工作,不能表达还要多久;如果任务超过几秒且可测量,应升级为进度条或阶段状态。
- Determinate progress 适合上传、导入、导出、批处理、长计算和可计数步骤;没有真实进度时不要伪造百分比。
- Stale-while-refresh 场景应保留旧内容并显示局部刷新,而不是每次重新拉数据都把页面清成骨架屏。
- Streaming 场景应逐步替换稳定占位,保持局部结果可读;流未结束时不能展示 empty 或 final success。
- Skeleton 动效必须尊重 prefers-reduced-motion,shimmer 可降级为静态、轻微 fade 或无动画。
设计判断
选择加载模式时先回答五个问题:用户是否已经看到旧内容,等待是否超过 1 秒,反馈区域是整页还是局部,进度是否真实可测,用户能否取消/重试/离开。若答案不清楚,就先不要做视觉特效,而是补状态模型和文案。
实现建议
建立 LoadMode 与 LoadStatus:none、skeleton、inline、spinner、determinate、stage、optimistic、stale-refresh、streaming;idle、pending、success、error、cancelled。模型应包含 label、regionLabel、progress(value/max/unit)、stage(current/steps)、staleContentVisible、disableDuplicateAction、canCancel、canRetry、reducedMotionFallback。空状态必须独立判断,例如 status === "success" && items.length === 0;错误状态独立渲染 retry 或 recovery;取消状态不要伪装成错误。用 min-height、aspect-ratio、固定表格行 skeleton 和保留按钮宽度降低 layout shift。React 中把 skeleton 放在数据边界附近,不要用一个全局 Suspense fallback 清空整个 app shell。
可访问性
被替换区域设置 aria-busy;determinate progress 设置可理解的 label 与 aria-valuenow/min/max;搜索、筛选、刷新完成用 polite live region;紧急失败才使用 assertive 或 alert。Skeleton placeholder 本身通常不应被读屏器逐个朗读。按钮 pending 时禁用重复提交,但要保留可理解的状态文案,例如 Saving、Saved、Save failed。
反例/风险
小于 1 秒的请求闪出 skeleton 导致界面抖动;刷新列表时清空旧数据;上传任务只放无限 spinner;按钮文字从 Save 变成 Saving 后宽度跳动;skeleton 比真实内容更醒目;动效忽略 reduced motion;伪造 87% 这种假进度;请求失败后 spinner 永远旋转;读屏器反复听到 Loading Loading Loading。
案例分析
NN/g 的 skeleton screen 分析指出,骨架屏通过模拟页面结构降低等待感和认知负担,但 frame-only skeleton 不推荐,因为它不能说明内容结构。Carbon 的 Loading pattern 把 skeleton、loading indicator 和 progressive loading 分开,并要求 skeleton 用在 container/data-based 组件上,不要代表 toast、dropdown、modal 或 loader 本身;Carbon inline loading 还定义 active、finished、error 和禁用重复提交。Material/Android progress 指南区分 determinate 与 indeterminate:可测任务显示完成度,未知任务只表示仍在处理。三者合起来证明:加载状态必须先做任务分类,再做视觉表现。
来源链接
NN/g Skeleton Screens 101: https://www.nngroup.com/articles/skeleton-screens/
NN/g Website Response Times: https://www.nngroup.com/articles/website-response-times/
Carbon Loading Pattern: https://carbondesignsystem.com/patterns/loading-pattern/
Carbon Loading Component: https://carbondesignsystem.com/components/loading/usage/
Carbon Inline Loading: https://carbondesignsystem.com/components/inline-loading/usage/
Android / Material Progress Indicators: https://developer.android.com/develop/ui/compose/components/progress
Agent 指令
先判断操作范围、时长和可测进度,再输出 loading mode、状态机、文案/live region、layout stability 规则、reduced-motion fallback、error/success/cancel handoff 和桌面/移动 QA。