骨架屏(Skeleton Screen)是一种 加载占位符技术,在页面或数据还在加载时,先显示页面的基本结构和轮廓,用户能提前了解页面布局,给用户一种「内容正在加载」的感觉,而不是空白页面。
核心作用:减少用户等待时的焦虑感,提升感知性能,让加载过程更流畅自然。
电商、图片社交平台等内容密集型页面,图片加载慢时,用骨架块占位:
<!-- 骨架屏状态:显示灰色占位块 -->
<div class="skeleton-image-container">
<div class="bg-gray-200 animate-pulse rounded-md" style="height: 200px;"></div>
</div>
<!-- 加载完成后:显示真实图片 -->
<div class="skeleton-image-container loaded">
<img src="真实图片URL" class="rounded-md transition-opacity duration-500">
</div>
用户资料页面,姓名、头像、简介等信息模块分别占位:
<div class="user-card">
<!-- 头像骨架 -->
<div class="skeleton-avatar"></div>
<!-- 姓名骨架 -->
<div class="skeleton-line w-3/4"></div>
<!-- 简介骨架 -->
<div class="skeleton-line w-1/2"></div>
</div>
文章、新闻页面,标题和段落分别加载:
<article class="article">
<!-- 标题骨架 -->
<h1 class="skeleton-title"></h1>
<!-- 段落骨架 -->
<p class="skeleton-paragraph"></p>
<p class="skeleton-paragraph"></p>
</article>
用 animate-pulse 制作闪烁效果,配合灰色背景模拟加载状态:
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Tailwind 内置了脉冲动画,直接添加类名即可:
<div class="bg-gray-200 rounded-md animate-pulse">
<!-- 占位内容 -->
</div>
完整例子:带骨架屏的图片卡片
<div class="max-w-sm mx-auto bg-white shadow-md rounded-lg overflow-hidden">
<!-- 加载中:骨架屏 -->
<div class="h-48 bg-gray-200 animate-pulse"></div>
<div class="p-4">
<!-- 加载中:骨架文本 -->
<div class="h-4 bg-gray-200 rounded animate-pulse mb-2"></div>
<div class="h-4 bg-gray-200 rounded animate-pulse w-3/4"></div>
</div>
</div>
用 JavaScript 控制显示状态,数据加载前显示骨架屏,加载完成后隐藏:
// React 示例
function UserCard({ user, loading }) {
if (loading) {
return (
<div className="user-card">
<div className="skeleton-avatar"></div>
<div className="skeleton-text"></div>
</div>
);
}
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
</div>
);
}
骨架屏的占位块大小应尽量接近真实内容(避免加载完成后跳动)
#f0f0f0),不能是纯白(对比太强)#e0e0e0),营造层次感| 方式 | 表现 | 用户体验 |
|---|---|---|
| 空白页面 | 一片空白,什么都没有 | ❌ 焦虑,不知道要等多久 |
| Spinner | 转圈圈,只有进度指示 | ⚠️ 知道在加载,但不知道内容结构 |
| 骨架屏 | 显示页面轮廓,内容占位 | ✅ 知道要加载什么,心理有数 |
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<div class="max-w-md mx-auto bg-white shadow-md rounded-lg overflow-hidden" id="card">
<!-- 初始状态:骨架屏 -->
<div class="h-64 bg-gray-200 animate-pulse" id="skeleton-image"></div>
<div class="p-4">
<div class="h-6 bg-gray-200 rounded animate-pulse mb-2" id="skeleton-title"></div>
<div class="h-4 bg-gray-200 rounded animate-pulse w-5/6 mb-2"></div>
<div class="h-4 bg-gray-200 rounded animate-pulse w-4/6"></div>
</div>
</div>
<script>
// 模拟加载延迟
setTimeout(() => {
// 隐藏骨架屏,显示真实内容
document.getElementById('skeleton-image').className = 'h-64 transition-all duration-500';
document.getElementById('skeleton-image').style.background = 'none';
// 加载图片
const img = document.createElement('img');
img.src = 'https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?q=80&w=2070&auto=format&fit=crop';
img.className = 'h-64 w-full object-cover';
img.onload = () => {
document.getElementById('skeleton-image').innerHTML = '';
document.getElementById('skeleton-image').appendChild(img);
};
// 显示文字内容
document.getElementById('skeleton-title').className = 'h-6 text-xl font-bold text-gray-800 transition-all duration-500';
document.getElementById('skeleton-title').innerHTML = '《代码大全》';
document.getElementById('card').querySelectorAll('#skeleton-title + div').forEach((el, idx) => {
el.className = idx === 0 ? 'h-4 text-gray-600 mb-2' : 'h-4 text-gray-600';
el.innerHTML = idx === 0 ? '软件开发的实用指南' : '程序员必读经典';
});
}, 2000); // 2秒后加载完成
</script>
</body>
</html>
效果:页面先显示灰色占位块(骨架屏),2秒后显示真实的 Unsplash 书籍图片和文字内容,让用户清楚知道页面在加载什么内容。