骨架屏是用灰色块模拟真实卡片布局结构的占位方案,非动效图标;它通过静态html+css直出、严格对齐样式与尺寸、三态逻辑控制显隐,提升首屏可预期性与渲染稳定性。

什么是骨架屏,为什么不用 loading 图标
骨架屏不是动效加载图标,而是用灰色块模拟真实卡片的布局结构,在数据还没回来时先占位。用户能立刻感知页面结构,比转圈图标更可预期;首屏渲染快、不闪跳,尤其适合 SSR 或 React/Vue 的服务端渲染场景。
- 骨架屏本质是静态 HTML + CSS,不依赖 JS 渲染,所以首屏直出快
- 不要等
fetch完再插入骨架——它得和容器一起 HTML 直出,或用display: none切换显隐 - 真实卡片有圆角、阴影、图片宽高比?骨架屏必须严格对齐,否则加载后会“跳”
纯 HTML + CSS 实现骨架卡片(无 JS)
核心思路:用 <div> 模拟标题、段落、头像、图片区域,靠 <code>background + linear-gradient 做灰色渐变动画。
<div class="skeleton-card"> <div class="skeleton-avatar"></div> <div class="skeleton-title"></div> <div class="skeleton-desc"></div> <div class="skeleton-desc short"></div> </div>
对应 CSS 要点:
- 所有骨架元素设
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%) - 加
background-size: 200% 100%和animation: loading 1.5s infinite让灰条流动 -
.skeleton-avatar用border-radius: 50%,.skeleton-title设固定高度(如height: 20px),避免重排
注意:别用 width: 100% 填满父容器——真实卡片往往有 max-width 或 padding,骨架也得一致,否则加载后宽度突变。
Vue/React 中控制骨架显隐的边界条件
骨架屏不是“加载中就显示”,而是在「数据未就绪且尚未报错」时才显示。常见错误逻辑:
- 错误:用
v-if="loading"控制骨架 → 一旦loading = false就立刻切真实内容,但此时data可能还是空 - 正确:用三态判断:
isLoading && !data显示骨架,!isLoading && data显示内容,!isLoading && error显示错误
在 Vue 中推荐这样写:
<template><div v-if="isLoading && !article">
<skeletoncard></skeletoncard>
</div>
<articlecard v-else-if="article" :data="article"></articlecard><errorblock v-else-if="error"></errorblock></template>
React 同理,别只依赖 useState({ loading: true }),要拆开管理 data、loading、error 三个状态。
响应式骨架卡片容易被忽略的坑
PC 卡片横排三列,移动端变成单列?骨架屏也得响应式,但不能只靠 @media 改 width。
- 图片区域的宽高比必须保持一致:PC 用
aspect-ratio: 16/9,移动端改aspect-ratio: 4/3会导致骨架和真实图比例不一致,加载后“缩一下” - 更稳妥的做法:让骨架卡片容器和真实卡片共用同一套 CSS 类(比如都用
.card),仅通过子元素 class 区分内容/骨架,避免样式割裂 - 如果用了 CSS-in-JS(如 Emotion),确保骨架组件的 className 不被 hash 冲突,否则媒体查询失效
真实项目里最常出问题的是:开发时只看桌面端,上线后发现 SkeletonCard 在 iPhone 上高度塌陷——因为忘了给 .skeleton-img 设置 min-height 或 aspect-ratio fallback。
骨架屏不是炫技,是细节堆出来的体验。最麻烦的永远不是怎么画灰块,而是让它和真实卡片在所有尺寸、所有状态、所有加载时机下,严丝合缝地对齐。











