白屏与布局跳动:骨架屏什么时候用、怎么做才不像摆设

骨架屏的实操指南:它解决的是「布局可预期」而不是「等待变短」;哪些区域值得做骨架、DOM 到灰条/灰块的映射规则、shimmer 与 pulse 动画的取舍、显示时长与闪烁阈值,以及把现有 HTML 一键转成骨架的安全做法。

约 6 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

骨架屏解决的是「可预期」,不是「变快」

实测中骨架屏不会让接口更快;它的价值是让用户提前看到布局结构,降低「页面坏了」的焦虑,并把加载后的布局跳动(CLS)压到接近 0。所以判断要不要做骨架,先看这块区域加载后会不会移动物体:会移动(卡片流、详情页头部)就值得做;不变(一行文字按钮)转圈就够了。

原始元素骨架形态要点
标题 / 段落文本圆角灰条,宽度按字数估多行时最后一行做短,模拟自然段落
图片 / 视频等宽高比的灰块锁定 aspect-ratio,加载后不跳
按钮 / 链接圆角块保留原 padding,尺寸才对得上
整个卡片灰块组合结构对齐原卡片,间距一致
图标小方块或省略太小的元素做骨架反而显乱

两档动画就够,别做第三档

shimmer(扫光)适合大面积卡片,方向感强、暗示「正在加载」;pulse(呼吸)适合小面积、列表行。同一页面只用一档,混用会显得乱。用户开了「减少动态效果」时必须降级为静态灰块——这不是可选项,是规范要求。

shimmer:渐变扫光,性能安全的写法
.sk { position: relative; overflow: hidden; background: #e5e7eb; border-radius: 6px; }
.sk::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.65), transparent);
  transform: translateX(-100%); animation: sk-sweep 1.4s infinite; }
@keyframes sk-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .sk::after { animation: none; content: none; } }
安全转换:把真实 HTML 变骨架前先剥离事件
骨架化用户 HTML 时务必:
1. 剥掉 <script> 与 on* 事件属性,避免注入
2. 文本节点按长度估宽,图片按 aspect-ratio 占位
3. 节点数设上限(如 800),防止巨型文档卡死主线程

阈值与反模式

  • 300ms 内能完成的请求别显示骨架:闪一下反而像 bug,宁可延迟 300ms 再出现
  • 骨架和真实内容的结构必须对齐:骨架是三行卡片、真内容是两行,加载完成瞬间照样跳动
  • 别整页铺满骨架:首屏关键区做骨架,页脚等静态区本来就有内容
  • 骨架期间要禁交互(覆盖透明层),防止用户点了个不存在的按钮
  • 超时兜底:8–10 秒仍未加载完,骨架换成错误态和重试按钮,别让扫光转一整天

常见问题

骨架屏会不会影响 SEO?

服务端渲染时输出的骨架只是初始 HTML,真实内容随后注入,搜索引擎渲染时会执行 JS 看到最终内容。真正要避免的是「永远只有骨架」——内容必须能在无交互的情况下到达 DOM。

shimmer 动画掉帧怎么办?

确认动画只动 transform 和 opacity(合成层属性),别动 background-position 或 width;扫光层用 ::after 加 will-change: transform 即可稳定 60fps。

React/Vue 里怎么组织骨架组件?

把灰条灰块做成无逻辑的展示组件(Skeleton/SkeletonText),业务组件里 loading ? : 。样式集中一处,别在每个组件里复制渐变代码。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件