Skeleton 骨架屏
内容加载前的占位。三形态 + 微光动画,也可作加载闸门。
三种形态
vue
<SrSkeleton variant="text" :rows="3" />
<SrSkeleton variant="rect" width="240px" height="80px" />
<SrSkeleton variant="circle" />| 形态 | 用途 |
|---|---|
text | 段落文本,rows 控制行数 |
rect | 图片、卡片、图表 |
circle | 头像 |
作加载闸门
最常用的方式:loading 为真时显示骨架,为假时渲染真实内容。
vue
<script setup>
const loading = ref(true)
onMounted(async () => {
await fetchData()
loading.value = false
})
</script>
<template>
<SrSkeleton :loading="loading" variant="text" :rows="2">
<p>{{ content }}</p>
</SrSkeleton>
</template>骨架的布局要接近真实内容
如果骨架是 3 行文字、真实内容是 6 行,切换时会明显跳一下—— 用户刚建立的空间感被打破了。
骨架的价值在于"减少布局跳动",形状差太远就失去了意义。 最好的做法是按真实内容的尺寸来设计骨架。
组合出真实布局
骨架屏很少单独用——通常是几个拼出目标内容的轮廓。
vue
<div style="display:flex; gap:16px">
<SrSkeleton variant="circle" />
<div style="flex:1">
<SrSkeleton variant="text" width="40%" />
<SrSkeleton variant="text" width="70%" />
</div>
</div>这比给 text 传 rows="3" 更像真实内容——逐行宽度不同才符合文本的自然形态。 所有行等宽的骨架看起来像表格,不像段落。
微光动画
animated 默认开启,是一条从左向右扫过的微光。
上方为无动画、下方为默认微光
vue
<SrSkeleton :animated="false" />动画在减少动态效果下会自动关闭
prefers-reduced-motion: reduce 时微光停止,但骨架本身保留—— 静态的灰块同样是"内容还没来"的有效信号。
这点和 SrTimeline 的 pending 光环一样: 降级时去掉动画,不要去掉信息。
尺寸
width / height 接受任意 CSS 长度。
vue
<SrSkeleton variant="rect" width="100%" height="40px" />
<SrSkeleton variant="rect" width="12rem" height="60px" />用百分比与 rem 而非像素,让骨架随容器自适应—— 写死像素的话,窄屏上会溢出去。
Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
loading | boolean | true | 为真时显示骨架,为假时渲染默认插槽 |
variant | 'text' | 'circle' | 'rect' | 'text' | 形态 |
rows | number | 1 | 文本行数(仅 text) |
width | string | — | 宽度 |
height | string | — | 高度 |
animated | boolean | true | 是否显示微光动画 |
插槽
| 名称 | 说明 |
|---|---|
default | 加载完成后渲染的真实内容 |
无障碍
- 骨架容器带
aria-busy="true",读屏软件会知道内容仍在加载, 不会把占位内容当成真实数据读出来 - 骨架的灰块加
aria-hidden="true",避免被朗读成无意义的元素 - 加载完成时若内容是重点(如搜索结果),用
aria-live="polite"播报一次 「已加载 N 条结果」,而不是让用户自己发现内容变了