Skip to content

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>

这比给 textrows="3" 更像真实内容——逐行宽度不同才符合文本的自然形态。 所有行等宽的骨架看起来像表格,不像段落。

微光动画

animated 默认开启,是一条从左向右扫过的微光。

上方为无动画、下方为默认微光

vue
<SrSkeleton :animated="false" />

动画在减少动态效果下会自动关闭

prefers-reduced-motion: reduce 时微光停止,但骨架本身保留—— 静态的灰块同样是"内容还没来"的有效信号。

这点和 SrTimelinepending 光环一样: 降级时去掉动画,不要去掉信息。

尺寸

width / height 接受任意 CSS 长度。

vue
<SrSkeleton variant="rect" width="100%" height="40px" />
<SrSkeleton variant="rect" width="12rem" height="60px" />

用百分比与 rem 而非像素,让骨架随容器自适应—— 写死像素的话,窄屏上会溢出去。

Props

名称类型默认值说明
loadingbooleantrue为真时显示骨架,为假时渲染默认插槽
variant'text' | 'circle' | 'rect''text'形态
rowsnumber1文本行数(仅 text
widthstring宽度
heightstring高度
animatedbooleantrue是否显示微光动画

插槽

名称说明
default加载完成后渲染的真实内容

无障碍

  • 骨架容器带 aria-busy="true",读屏软件会知道内容仍在加载, 不会把占位内容当成真实数据读出来
  • 骨架的灰块加 aria-hidden="true",避免被朗读成无意义的元素
  • 加载完成时若内容是重点(如搜索结果),用 aria-live="polite" 播报一次 「已加载 N 条结果」,而不是让用户自己发现内容变了

自用组件库 · 源码分发 · 不发布 npm