Skeleton 骨架屏
在需要等待加载内容的位置,提供一个可视化的占位符,可以提升用户体验。
基础用法
基础的骨架效果。
<template>
<el-skeleton />
<br />
<el-skeleton style="--el-skeleton-circle-size: 100px">
<template #template>
<el-skeleton-item variant="circle" />
</template>
</el-skeleton>
</template>
可配置的行数
您可以自行配置骨架屏的行数,以便更精准地适配您的场景。需要注意的是,实际渲染的行数会比您配置的 `rows` 值多 1,因为我们多渲染了宽度为 33% 的一个标题行。
动画效果
我们提供了一个开关 `animated`,用于控制是否显示加载动画。当这个值为 `true` 时,`el-skeleton` 的所有子项都会显示动画。
自定义模板
Element Plus 只提供了最常用的模板,有时候这可能无法满足您的需求,因此您可以使用名为 `template` 的插槽来自定义模板。
同时,我们也提供了不同类型的骨架单元供您选择,更详细的信息请滚动到本页底部查看 API 描述。此外,在构建您自己的自定义骨架结构时,您应该使其结构尽可能接近真实 DOM,这样可以避免因高度差异引起的 DOM 抖动。
<template>
<el-skeleton style="width: 240px">
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 240px" />
<div style="padding: 14px">
<el-skeleton-item variant="p" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</template>
</el-skeleton>
</template>
加载状态
当 `Loading` 结束时,我们总是需要向最终用户展示带有数据的真实 UI。通过 `loading` 属性,我们可以控制是否显示 DOM。您也可以使用 `default` 插槽来构建真实的 DOM 元素。
<template>
<el-space direction="vertical" alignment="flex-start">
<div>
<label style="margin-right: 16px">Switch Loading</label>
<el-switch v-model="loading" />
</div>
<el-skeleton style="width: 240px" :loading="loading" animated>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 240px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</template>
<template #default>
<el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
<img
src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
class="image"
/>
<div style="padding: 14px">
<span>Delicious hamburger</span>
<div class="bottom card-header">
<div class="time">{{ currentDate }}</div>
<el-button text class="button">Operation button</el-button>
</div>
</div>
</el-card>
</template>
</el-skeleton>
</el-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(true)
const currentDate = new Date().toDateString()
</script>
渲染列表数据
大多数情况下,骨架屏被用作渲染尚未从服务器获取的列表数据的指示器,这时我们需要凭空创建一列骨架屏,使其看起来像正在加载。通过 `count` 属性,您可以控制需要渲染到浏览器的模板数量。
提示
我们不建议向浏览器渲染大量的伪 UI,这仍然会导致性能问题,并且销毁骨架屏也需要更长的时间。为了更好的用户体验,请将 `count` 保持在尽可能小的范围内。
<template>
<el-space style="width: 100%" fill>
<div>
<el-button @click="setLoading">Click me to reload</el-button>
</div>
<el-skeleton
style="display: flex; gap: 8px"
:loading="loading"
animated
:count="3"
>
<template #template>
<div style="flex: 1">
<el-skeleton-item variant="image" style="height: 240px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</div>
</template>
<template #default>
<el-card
v-for="item in lists"
:key="item.name"
:body-style="{ padding: '0px', marginBottom: '1px' }"
>
<img
:src="item.imgUrl"
class="image multi-content"
style="max-width: 100%"
/>
<div style="padding: 14px">
<span>{{ item.name }}</span>
<div class="bottom card-header">
<div class="time">{{ currentDate }}</div>
<el-button text class="button">Operation button</el-button>
</div>
</div>
</el-card>
</template>
</el-skeleton>
</el-space>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
interface ListItem {
imgUrl: string
name: string
}
const loading = ref(true)
const lists = ref<ListItem[]>([])
const currentDate = new Date().toDateString()
const setLoading = () => {
loading.value = true
setTimeout(() => {
loading.value = false
}, 2000)
}
onMounted(() => {
loading.value = false
lists.value = [
{
imgUrl:
'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
name: 'Deer',
},
{
imgUrl:
'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
name: 'Horse',
},
{
imgUrl:
'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
name: 'Mountain Lion',
},
]
})
</script>
避免渲染抖动
有时 API 响应非常快,当这种情况发生时,骨架屏刚渲染到 DOM 就需要切换回真实的 DOM,这会导致突然的闪烁。为了避免这种情况,您可以使用 `throttle` 属性。
提示
自从 2.8.8 版本起,`throttle` 属性支持两个值:`number` 和 `object`。当传递一个 `number` 时,它等同于 `{leading: xxx}`,用于控制骨架屏显示的节流。当然,您也可以通过传递 `{trailing: xxx}` 来控制骨架屏消失的节流。

<template>
<el-space direction="vertical" alignment="flex-start">
<div>
<label style="margin-right: 16px">Switch Loading</label>
<el-switch v-model="loading" />
</div>
<el-skeleton
style="width: 240px"
:loading="loading"
animated
:throttle="500"
>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 265px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</template>
<template #default>
<el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
<img
src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
class="image"
/>
<div style="padding: 14px">
<span>Delicious hamburger</span>
<div class="bottom card-header">
<div class="time">{{ currentDate }}</div>
<el-button text class="button">operation button</el-button>
</div>
</div>
</el-card>
</template>
</el-skeleton>
</el-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(false)
const currentDate = new Date().toDateString()
</script>
初始渲染加载 2.8.8
当 loading 的初始值为 true 时,您可以设置 `throttle: {initVal: true, leading: xxx}` 来控制初始骨架屏立即显示而无需节流。
<template>
<el-space direction="vertical" alignment="flex-start">
<div>
<label style="margin-right: 16px">Switch Loading</label>
<el-switch v-model="loading" />
</div>
<el-skeleton
style="width: 240px"
:loading="loading"
animated
:throttle="{ leading: 500, initVal: true }"
>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 265px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</template>
<template #default>
<el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
<img
src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
class="image"
/>
<div style="padding: 14px">
<span>Delicious hamburger</span>
<div class="bottom card-header">
<div class="time">{{ currentDate }}</div>
<el-button text class="button">operation button</el-button>
</div>
</div>
</el-card>
</template>
</el-skeleton>
</el-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(true)
const currentDate = new Date().toDateString()
</script>
切换显示/隐藏时避免渲染抖动 2.8.8
提示
您可以设置 `throttle: {initVal: true, leading: xxx, trailing: xxx}` 来控制骨架屏效果的初始显示,并在切换加载状态时使骨架屏的过渡更加平滑。
有时您希望在加载状态切换显示或隐藏时,业务组件的渲染更加平滑。您可以使用 `throttle: {leading: xxx, trailing:xxx}` 来控制渲染抖动。
<template>
<el-space direction="vertical" alignment="flex-start">
<div>
<label style="margin-right: 16px">Switch Loading</label>
<el-switch v-model="loading" />
</div>
<el-skeleton
style="width: 240px"
:loading="loading"
animated
:throttle="{ leading: 500, trailing: 500, initVal: true }"
>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 265px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" style="width: 50%" />
<div
style="
display: flex;
align-items: center;
justify-items: space-between;
margin-top: 16px;
height: 16px;
"
>
<el-skeleton-item variant="text" style="margin-right: 16px" />
<el-skeleton-item variant="text" style="width: 30%" />
</div>
</div>
</template>
<template #default>
<el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
<img
src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
class="image"
/>
<div style="padding: 14px">
<span>Delicious hamburger</span>
<div class="bottom card-header">
<div class="time">{{ currentDate }}</div>
<el-button text class="button">operation button</el-button>
</div>
</div>
</el-card>
</template>
</el-skeleton>
</el-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(false)
const currentDate = new Date().toDateString()
</script>
Skeleton API
Skeleton 属性
名称 | 描述 | 类型 | 默认值 |
---|---|---|---|
animated | 是否显示动画 | boolean | false |
count | 要渲染的伪 UI 条目数量 | number | 1 |
加载中 | 是否显示真实 DOM | boolean | false |
行数 | 行数,仅在没有提供 template 插槽时有效 | number | 3 |
throttle | 渲染延迟的毫秒数。数字表示延迟显示,也可以设置为延迟隐藏,例如 `{ leading: 500, trailing: 500 }`。当需要控制 loading 的初始值时,可以设置 `{ initVal: true }` | number / object | 0 |
Skeleton 插槽
名称 | 描述 | 作用域 |
---|---|---|
default | 真实渲染的 DOM | object |
template | 用作渲染骨架屏模板的内容 | object |
SkeletonItem API
SkeletonItem 属性
名称 | 描述 | 类型 | 默认值 |
---|---|---|---|
variant | 当前渲染的骨架屏类型 | 枚举 | 文本 |