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-skeleton与el-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-item(variant="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 />当animated为true时,el-skeleton根节点会挂上is-animated类(见 skeleton.vue 中的ns.is('animated', animated)),所有子级骨架单元都会呈现流动的浅色渐变动画效果。该属性默认值为false。完整的可运行示例见 animation.vue。
自定义模板:template 插槽与 variant
Element Plus 只提供了最常见的默认模板,当默认结构无法满足需求时,可以使用template插槽自由拼装,配合el-skeleton-item的variant属性选择不同的骨架单元形态:
| 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支持number和object两种形式:
- 传入数字时,等价于
{ 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)
当loading在true/false之间反复切换时,可以同时指定leading与trailing,让骨架屏的出现与消失都经过节流缓冲,从而避免切换过程中的渲染跳动(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 延迟 }关键点:
throttle为 0 时直接透传,不引入任何延迟,此时组件表现等同无节流;initVal决定初始状态:当loading初始为true且设置了initVal: true,内部节流状态的初始值即为true,骨架屏首帧就显示,绕开leading的延迟;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时可以遵循以下原则:
- 静态占位:直接使用
rows+ 默认模板,或通过template插槽 +el-skeleton-item的variant拼装与真实 DOM 近似的结构; - 数据列表:用
count生成多条占位,配合v-for渲染的真实列表切换; - 交互体验:
animated开启动画;用throttle(number或{ leading, trailing, initVal })消除快速响应下的闪烁与切换跳动; - 性能:控制
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),仅供参考