骨架屏

骨架屏:在真实内容加载前显示内容结构占位,提升用户感知速度。

#type / concept #status / growing #tech / dev / frontend

[!info] related notes

骨架屏

一句话定义

骨架屏是在页面加载时显示与真实布局匹配的灰色占位块,通过渐进展示内容结构来提升用户感知性能的优化技术。

核心机制 / 工作原理

与 Loading Spinner 对比

维度骨架屏Loading Spinner
用户感知”内容正在来""不知道在等什么”
布局信息提前展示页面结构完全无信息
跳变风险低(尺寸已预留)高(内容突然出现)
适用场景内容结构可预测结构未知或简单等待

实现方式

CSS 渐变动画(shimmer)

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 4px;
}

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

React 骨架屏组件

function SkeletonCard() {
  return (
    <div className="card">
      <div className="skeleton" style={{ width: '100%', height: 180 }} />
      <div className="skeleton" style={{ width: '60%', height: 20, marginTop: 12 }} />
      <div className="skeleton" style={{ width: '80%', height: 14, marginTop: 8 }} />
    </div>
  );
}

条件渲染

function ArticlePage() {
  const { data, loading } = useFetch('/api/article');
  if (loading) return <ArticleSkeleton />;
  return <ArticleContent data={data} />;
}

设计原则

  1. 匹配真实布局:骨架块的尺寸、间距应与实际内容一致
  2. 使用 shimmer 动画:纯色块太死板,微动画暗示”正在加载”
  3. 避免纯白/纯黑:用浅灰色系,有”占位”的视觉暗示
  4. 预留正确尺寸:图片、文字区域的高度要准确,防止内容加载后跳变
  5. 渐进消失:内容到达后逐区域替换,而非整体闪烁切换

应用场景

  • 列表页(Feed、搜索结果)
  • 详情页(文章、商品详情)
  • 卡片流(社交动态、瀑布流)
  • 表单页(复杂表单加载配置后填充)
  • 首屏加载(SPA 首次进入)

最小例子

一个最简单的列表骨架屏:

<!-- 骨架状态 -->
<div class="list-item skeleton">
  <div class="skeleton-avatar"></div>
  <div class="skeleton-text"></div>
</div>

<!-- 真实内容替换 -->
<div class="list-item">
  <img src="avatar.jpg" />
  <p>真实文本内容</p>
</div>

CSS 中 .skeleton-avatar.skeleton-text 使用 shimmer 动画样式,尺寸与真实元素一致。

边界与常见误解

  • 骨架屏不是性能优化:不减少加载时间,只改善感知;真正优化还需懒加载、缓存等手段
  • 骨架屏 ≠ 占位图(placeholder):placeholder 是低质量内容预览(如模糊缩略图),骨架屏是结构占位
  • CLS 风险:如果骨架尺寸与真实内容不匹配,加载完成时会导致布局偏移,反而恶化 CLS
  • 过度设计:简单场景用 Loading Spinner 即可,不需要每个页面都做骨架屏
  • SEO 影响:骨架屏是客户端渲染的,搜索引擎爬虫看到的可能是空壳;需配合 SSR/SSG
创建于 2026/4/6 更新于 2026/5/27