Skeleton 骨架屏

在需要等待加载内容的位置设置一个骨架屏,某些场景下比 Loading 的视觉效果更好。

基础用法

基础的骨架效果。

Skeleton 骨架屏 - 图1

  1. <template>
  2. <el-skeleton />
  3. <br />
  4. <el-skeleton style="--el-skeleton-circle-size: 100px">
  5. <template #template>
  6. <el-skeleton-item variant="circle" />
  7. </template>
  8. </el-skeleton>
  9. </template>

更多参数

可以配置骨架屏段落数量,以便更接近真实渲染效果。显示的数量会比传入的数量多 1,首行会被渲染一个长度 33% 的段首。

Skeleton 骨架屏 - 图2

  1. <template>
  2. <el-skeleton :rows="5" />
  3. </template>

动画效果

我们提供了一个开关标志,表明是否显示加载动画, 调用 animated 如果真真是这样,所有的 el-skeleton 的子节点将显示动画。

Skeleton 骨架屏 - 图3

  1. <template>
  2. <el-skeleton :rows="5" animated />
  3. </template>

自定义样式

Element Plus 提供的排版模式有时候并不满足要求,当您想要用自己定义的模板时,可以通过一个具名 Slot template 来自己设定模板。

我们提供了不同的模板单元可供使用,具体可选值请看 API 详细描述。 另外,在构建您自己自定义的骨架时,您应该尽可能更接近于真正的DOM。 避免DOM因高度差而发生抖动。

Skeleton 骨架屏 - 图4

  1. <template>
  2. <el-skeleton style="width: 240px">
  3. <template #template>
  4. <el-skeleton-item variant="image" style="width: 240px; height: 240px" />
  5. <div style="padding: 14px">
  6. <el-skeleton-item variant="p" style="width: 50%" />
  7. <div
  8. style="
  9. display: flex;
  10. align-items: center;
  11. justify-items: space-between;
  12. "
  13. >
  14. <el-skeleton-item variant="text" style="margin-right: 16px" />
  15. <el-skeleton-item variant="text" style="width: 30%" />
  16. </div>
  17. </div>
  18. </template>
  19. </el-skeleton>
  20. </template>

加载状态

Loading 结束之后,我们往往需要显示真实的 UI, 可以通过 loading 的值来控制是否显示加载后的 DOM。 然后通过 具名 Slot default 来设置当 loading 结束之后需要展示的 UI。

Skeleton 骨架屏 - 图5

  1. <template>
  2. <el-space direction="vertical" alignment="flex-start">
  3. <div>
  4. <label style="margin-right: 16px">Switch Loading</label>
  5. <el-switch v-model="loading" />
  6. </div>
  7. <el-skeleton style="width: 240px" :loading="loading" animated>
  8. <template #template>
  9. <el-skeleton-item variant="image" style="width: 240px; height: 240px" />
  10. <div style="padding: 14px">
  11. <el-skeleton-item variant="h3" style="width: 50%" />
  12. <div
  13. style="
  14. display: flex;
  15. align-items: center;
  16. justify-items: space-between;
  17. margin-top: 16px;
  18. height: 16px;
  19. "
  20. >
  21. <el-skeleton-item variant="text" style="margin-right: 16px" />
  22. <el-skeleton-item variant="text" style="width: 30%" />
  23. </div>
  24. </div>
  25. </template>
  26. <template #default>
  27. <el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
  28. <img
  29. src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
  30. class="image"
  31. />
  32. <div style="padding: 14px">
  33. <span>Delicious hamburger</span>
  34. <div class="bottom card-header">
  35. <div class="time">{{ currentDate }}</div>
  36. <el-button text class="button">Operation button</el-button>
  37. </div>
  38. </div>
  39. </el-card>
  40. </template>
  41. </el-skeleton>
  42. </el-space>
  43. </template>
  44. <script lang="ts" setup>
  45. import { ref } from 'vue'
  46. const loading = ref(true)
  47. const currentDate = new Date().toDateString()
  48. </script>

渲染多条数据

大多时候, 骨架屏都被用来渲染列表, 当我们需要在从服务器获取数据的时候来渲染一个假的 UI。 利用 count 这个属性就能控制渲染多少条假的数据在页面上

TIP

请注意, 请尽可能的将 count 的大小保持在最小状态, 即使是假的 UI, DOM 元素多了之后, 照样会引起性能问题, 并且在骨架屏销毁时所消耗的时间也会更长(相对来说)。

Skeleton 骨架屏 - 图6

  1. <template>
  2. <el-space direction="vertical" alignment="flex-start">
  3. <el-button @click="setLoading">Click me to reload</el-button>
  4. <el-skeleton style="width: 240px" :loading="loading" animated :count="3">
  5. <template #template>
  6. <el-skeleton-item variant="image" style="width: 400px; height: 267px" />
  7. <div style="padding: 14px">
  8. <el-skeleton-item variant="h3" style="width: 50%" />
  9. <div
  10. style="
  11. display: flex;
  12. align-items: center;
  13. justify-items: space-between;
  14. margin-top: 16px;
  15. height: 16px;
  16. "
  17. >
  18. <el-skeleton-item variant="text" style="margin-right: 16px" />
  19. <el-skeleton-item variant="text" style="width: 30%" />
  20. </div>
  21. </div>
  22. </template>
  23. <template #default>
  24. <el-card
  25. v-for="item in lists"
  26. :key="item.name"
  27. :body-style="{ padding: '0px', marginBottom: '1px' }"
  28. >
  29. <img :src="item.imgUrl" class="image multi-content" />
  30. <div style="padding: 14px">
  31. <span>{{ item.name }}</span>
  32. <div class="bottom card-header">
  33. <div class="time">{{ currentDate }}</div>
  34. <el-button text class="button">Operation button</el-button>
  35. </div>
  36. </div>
  37. </el-card>
  38. </template>
  39. </el-skeleton>
  40. </el-space>
  41. </template>
  42. <script lang="ts" setup>
  43. import { onMounted, ref } from 'vue'
  44. interface ListItem {
  45. imgUrl: string
  46. name: string
  47. }
  48. const loading = ref(true)
  49. const lists = ref<ListItem[]>([])
  50. const currentDate = new Date().toDateString()
  51. const setLoading = () => {
  52. loading.value = true
  53. setTimeout(() => {
  54. loading.value = false
  55. }, 2000)
  56. }
  57. onMounted(() => {
  58. loading.value = false
  59. lists.value = [
  60. {
  61. imgUrl:
  62. 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
  63. name: 'Deer',
  64. },
  65. {
  66. imgUrl:
  67. 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg',
  68. name: 'Horse',
  69. },
  70. {
  71. imgUrl:
  72. 'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg',
  73. name: 'Mountain Lion',
  74. },
  75. ]
  76. })
  77. </script>

防止渲染抖动

有的时候,API 的请求回来的特别快,往往骨架占位刚刚被渲染,真实的数据就已经回来了,用户的界面会突然一闪, 此时为了避免这种情况,就需要通过 throttle 属性来避免这个问题。

Skeleton 骨架屏 - 图7

  1. <template>
  2. <el-space direction="vertical" alignment="flex-start">
  3. <div>
  4. <label style="margin-right: 16px">Switch Loading</label>
  5. <el-switch v-model="loading" />
  6. </div>
  7. <el-skeleton
  8. style="width: 240px"
  9. :loading="loading"
  10. animated
  11. :throttle="500"
  12. >
  13. <template #template>
  14. <el-skeleton-item variant="image" style="width: 240px; height: 240px" />
  15. <div style="padding: 14px">
  16. <el-skeleton-item variant="h3" style="width: 50%" />
  17. <div
  18. style="
  19. display: flex;
  20. align-items: center;
  21. justify-items: space-between;
  22. margin-top: 16px;
  23. height: 16px;
  24. "
  25. >
  26. <el-skeleton-item variant="text" style="margin-right: 16px" />
  27. <el-skeleton-item variant="text" style="width: 30%" />
  28. </div>
  29. </div>
  30. </template>
  31. <template #default>
  32. <el-card :body-style="{ padding: '0px', marginBottom: '1px' }">
  33. <img
  34. src="https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png"
  35. class="image"
  36. />
  37. <div style="padding: 14px">
  38. <span>Delicious hamburger</span>
  39. <div class="bottom card-header">
  40. <div class="time">{{ currentDate }}</div>
  41. <el-button text class="button">operation button</el-button>
  42. </div>
  43. </div>
  44. </el-card>
  45. </template>
  46. </el-skeleton>
  47. </el-space>
  48. </template>
  49. <script lang="ts" setup>
  50. import { ref } from 'vue'
  51. const loading = ref(false)
  52. const currentDate = new Date().toDateString()
  53. </script>

Skeleton API

Skeleton 属性

属性说明类型默认值
animated是否使用动画booleanfalse
count渲染多少个 template, 建议使用尽可能小的数字number1
loading是否显示加载结束后的 DOM 结构booleanfalse
rows骨架屏段落数量number3
throttle延迟占位 DOM 渲染的时间, 单位是毫秒number0

Skeleton 插槽

插槽名说明作用域
default正在渲染的DOM$attrs
template自定义渲染 skeleton 模板{ key: number }

Skeleton Item API

Skeleton Item 属性

属性说明类型默认值
variant当前渲染 skeleton 类型‘p’ | ‘text’ | ‘h1’ | ‘h3’ | ‘caption’ | ‘button’ | ‘image’ | ‘circle’ | ‘rect’‘text’

源代码

组件 Skeleton 骨架屏 - 图8 文档 Skeleton 骨架屏 - 图9

贡献者

Skeleton 骨架屏 - 图10 三咲智子

Skeleton 骨架屏 - 图11 jeremywu

Skeleton 骨架屏 - 图12 云游君

Skeleton 骨架屏 - 图13 C.Y.Kun

Skeleton 骨架屏 - 图14 LIUCHAO

Skeleton 骨架屏 - 图15 blackie

Skeleton 骨架屏 - 图16 bqy

Skeleton 骨架屏 - 图17 on the field of hope

Skeleton 骨架屏 - 图18 porcelainHeart

Skeleton 骨架屏 - 图19 btea

Skeleton 骨架屏 - 图20 Hassan Tariq