Element Plus Skeleton 骨架屏组件完全指南:从基础占位到防抖渲染的实战解析
2026/9/10 15:03:02 网站建设 项目流程

Element Plus Skeleton 骨架屏组件完全指南:从基础占位到防抖渲染的实战解析

【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus

Skeleton(骨架屏)是 Element Plus(Vue 3 组件库)中用于加载态占位的关键组件,它能在数据尚未就绪时渲染出与真实界面结构近似的灰色占位骨架,避免页面空白与布局跳动,显著提升加载过程的视觉与交互体验。本文将基于 Element Plus 官方文档与仓库源码(skeleton.md),系统讲解el-skeletonel-skeleton-item的全部属性、插槽与使用场景,并深入throttle节流渲染的底层实现,帮助你写出"无闪烁、无跳动、体验顺滑"的加载态界面。

为什么需要骨架屏

当页面数据尚未从服务端返回时,常见的做法是展示一个全局 loading 转圈,或者干脆留白。前者无法传递真实页面的结构信息,后者会让用户误以为页面卡死。骨架屏的解决思路是:先渲染出与真实 DOM 高度近似的灰色占位块,让用户提前感知"这里会有一张图片、这里有几行文字、这里有一个按钮",从而降低等待焦虑。

在 Element Plus 中,el-skeleton提供了开箱即用的骨架屏能力,支持动画、自定义模板、列表渲染、防抖切换等一系列能力,接下来逐一展开。

基础用法

最简单的骨架屏无需任何配置,直接引入组件即可:

<el-skeleton />

默认情况下会渲染出 3 行占位段落(rows默认值为 3),第一行宽度约为其余行的 33%,起到"标题行"的视觉提示作用。

你也可以结合template插槽与el-skeleton-item拼出圆形头像等结构。例如通过 CSS 变量--el-skeleton-circle-size控制圆形骨架的尺寸:

<el-skeleton /> <br /> <el-skeleton style="--el-skeleton-circle-size: 100px"> <template #template> <el-skeleton-item variant="circle" /> </template> </el-skeleton>

完整的可运行示例见 basic-usage.vue。

可配置的行数:rows

rows用于控制默认模板中渲染的占位段落行数:

<el-skeleton :rows="5" />

需要注意文档中的一个关键细节:实际渲染的行数永远比传入的rows多 1。原因在于组件内部会额外渲染一行宽度为 33% 的"标题行"。从 skeleton.vue 的实现可以看到,默认模板由两部分组成:

  • 第一个el-skeleton-itemvariant="p",带is-first类)作为标题行;
  • 随后v-for循环渲染rows个段落行,最后一行带有is-last类。
<el-skeleton-item :class="ns.is('first')" variant="p" /> <el-skeleton-item v-for="item in rows" :key="item" :class="[ns.e('paragraph'), ns.is('last', item === rows && rows > 1)]" variant="p" />

也就是说传入:rows="5"时页面实际看到 6 行,其中首行更短、更像是标题。完整的可运行示例见 configurable-rows.vue。

加载动画:animated

为骨架屏开启呼吸式闪烁动画,只需添加animated布尔属性:

<el-skeleton :rows="5" animated />

animatedtrue时,el-skeleton根节点会挂上is-animated类(见 skeleton.vue 中的ns.is('animated', animated)),所有子级骨架单元都会呈现流动的浅色渐变动画效果。该属性默认值为false。完整的可运行示例见 animation.vue。

自定义模板:template 插槽与 variant

Element Plus 只提供了最常见的默认模板,当默认结构无法满足需求时,可以使用template插槽自由拼装,配合el-skeleton-itemvariant属性选择不同的骨架单元形态:

variant 取值形态说明
p段落行(默认值)
text短文本行
h1一级标题样式的粗短占位
h3三级标题样式的占位
caption说明性小字号占位
button按钮形占位
image图片形占位(可配合宽高样式)
circle圆形占位(头像/图标)
rect矩形占位

上述枚举在 skeleton-item.ts 中通过values明确限定,非法值不会被接受。

下面是一个仿"卡片"结构的自定义模板示例——上方为正方形图片占位,下方为标题行与两行文字占位:

<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>

最佳实践提示:构建自定义骨架结构时,应尽可能让骨架的 DOM 结构与真实内容 DOM 保持接近(例如占位块的高度、间距、布局层级尽量一致),这样可以避免加载完成切换时因高度差导致的页面跳动(DOM bouncing)。完整的可运行示例见 customized-template.vue。

加载状态切换:loading 与 default 插槽

数据加载完成后,需要把骨架屏切换回真实界面。通过loading属性(默认true)控制显示哪一侧,并通过default插槽放置真实 DOM:

<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、图片、按钮等 --> </template> </el-skeleton> </el-space>

从源码实现看,loading会经过useThrottleRender的节流处理后形成内部状态uiLoading(见 skeleton.vue),模板根据uiLoading决定渲染骨架层还是default插槽内容。完整的可运行示例见 loading-state.vue。

渲染数据列表:count

骨架屏最常见的应用场景是"列表数据加载中"的占位。count属性用于控制同一套模板重复渲染的次数,从而"凭空"生成多条骨架项,让列表看起来正在加载:

<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> <!-- v-for 渲染真实列表数据 --> </template> </el-skeleton>

真实数据到达后,把loading置为false并传入列表数据,骨架即可无缝切换为真实卡片。完整的可运行示例见 rendering-with-data.vue。

::: tip 性能建议 官方文档明确提示:不建议向浏览器渲染大量虚假 UI。过多的骨架项同样会造成性能问题,且销毁骨架也需要更长时间。请让count尽可能小,以获得更好的用户体验。 :::

避免渲染跳动:throttle 节流

当接口响应极快时,骨架屏刚渲染出来就要立刻切回真实 DOM,会出现一瞬的闪烁(flash),观感很差。为此el-skeleton提供了throttle属性,以毫秒为单位延迟骨架屏的显示,给"快速返回的数据"留出缓冲窗口:

<el-skeleton style="width: 240px" :loading="loading" animated :throttle="500"> <!-- template 与 default 插槽同上 --> </el-skeleton>

throttle 的两种取值形式(^2.8.8)

从 2.8.8 版本起,throttle支持numberobject两种形式:

  • 传入数字时,等价于{ leading: xxx },即控制骨架屏显示前的延迟
  • 传入对象{ trailing: xxx }时,可进一步控制骨架屏消失(隐藏)前的延迟

完整的可运行示例见 avoiding-rendering-bouncing.vue。

初始加载即显示:{ initVal: true }(^2.8.8)

loading的初始值为true时,如果直接设置throttle: 500,骨架屏也会被节流延迟显示,导致页面一开始没有骨架也没有内容。此时可以传入{ initVal: true, leading: xxx },让初始骨架屏立即显示、不受节流影响

<el-skeleton style="width: 240px" :loading="loading" animated :throttle="{ leading: 500, initVal: true }"> <!-- ... --> </el-skeleton>

完整的可运行示例见 initial-rendering-loading.vue。

平滑切换:{ leading, trailing, initVal }(^2.8.8)

loadingtrue/false之间反复切换时,可以同时指定leadingtrailing,让骨架屏的出现消失都经过节流缓冲,从而避免切换过程中的渲染跳动(rendering bouncing):

<el-skeleton style="width: 240px" :loading="loading" animated :throttle="{ leading: 500, trailing: 500, initVal: true }" > <!-- ... --> </el-skeleton>

这样配置后:初始骨架立即显示,之后每次切到加载态延迟 500ms 才出现骨架,每次切回真实内容也延迟 500ms,切换过程更平滑。完整的可运行示例见 leading-trailing-without-bouncing.vue。

throttle 的底层实现原理

throttle的节流逻辑并不在el-skeleton内部实现,而是复用 Element Plus 的useThrottleRenderHook(源码见 use-throttle-render/index.ts)。其核心思路如下:

export type ThrottleType = { leading?: number; trailing?: number; initVal?: boolean } | number export const useThrottleRender = ( loading: Ref<boolean>, throttle: ThrottleType = 0 ) => { if (throttle === 0) return loading const initVal = isObject(throttle) && Boolean(throttle.initVal) const throttled = ref(initVal) // ... 通过 watch + setTimeout 分别处理 leading / trailing 延迟 }

关键点:

  1. throttle为 0 时直接透传,不引入任何延迟,此时组件表现等同无节流;
  2. initVal决定初始状态:当loading初始为true且设置了initVal: true,内部节流状态的初始值即为true,骨架屏首帧就显示,绕开leading的延迟;
  3. leading/trailing分别挂钩显示与隐藏:通过watch监听loading变化,配合setTimeout延后更新内部状态,从而达成"延迟出现 / 延迟消失"的双向节流。

在 skeleton.vue 中,useThrottleRender(toRef(props, 'loading'), props.throttle)的返回值被命名为uiLoading,模板只认这个节流后的状态——这就是"节流切换无闪烁"的实现基础。同时uiLoading也通过defineExpose暴露给外部,方便在特殊场景下直接读取当前骨架屏的显示状态。

Skeleton API 速查

Skeleton Attributes

名称说明类型默认值
animated是否显示加载动画^[boolean]false
count渲染到 DOM 中的骨架项数量^[number]1
loading是否显示真实 DOM(false时渲染default插槽内容)^[boolean]true
rows行数,仅在没有提供template插槽时生效(实际渲染行数会比该值多 1,多出的为首行 33% 宽标题行)^[number]3
throttle渲染延迟(毫秒)。数字表示延迟显示,也可传入对象延迟隐藏,如{ leading: 500, trailing: 500 };需要控制loading初始值时设置{ initVal: true }^[number] / ^[object]{ leading?: number, trailing?: number, initVal?: boolean }0

需要说明的是,源码 skeleton.ts 中loading的 prop 默认值即为true,文档表格中写作false的默认值实际上被useThrottleRender与组件内部状态共同决定,实践中以"不传loading时默认显示骨架屏"为准。

Skeleton Slots

名称说明插槽参数
default真实渲染 DOM(加载完成后的内容)^[object]$attrs
template骨架屏模板内容^[object]{ key: number }(循环渲染时的序号)

SkeletonItem API

SkeletonItem Attributes

名称说明类型默认值
variant当前渲染的骨架单元类型^[enum]'p' \| 'text' \| 'h1' \| 'h3' \| 'caption' \| 'button' \| 'image' \| 'circle' \| 'rect'text

el-skeleton-item的类型定义位于 skeleton-item.ts,其variant枚举与样式类一一对应;对应的 SCSS 样式可在 theme-chalk/src/skeleton-item.scss 中查看每种变体的尺寸与圆角规则。

实战小结

综合文档与源码,使用el-skeleton时可以遵循以下原则:

  1. 静态占位:直接使用rows+ 默认模板,或通过template插槽 +el-skeleton-itemvariant拼装与真实 DOM 近似的结构;
  2. 数据列表:用count生成多条占位,配合v-for渲染的真实列表切换;
  3. 交互体验animated开启动画;用throttlenumber{ leading, trailing, initVal })消除快速响应下的闪烁与切换跳动;
  4. 性能:控制count大小,避免一次性渲染过多假 UI。

通过loading属性与default插槽的组合,el-skeleton将"加载占位"与"真实内容"统一封装在一个组件里,再配合useThrottleRender的节流机制,即可低成本地构建出专业、顺滑、无跳动的加载态界面。

【免费下载链接】element-plus🎉 A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询