资讯动态

Element UI Skeleton 骨架屏组件完全指南:从基础用法到源码级防闪烁优化

发布时间:2026/9/18 19:20:10 来源:尧图企业网站定制
Element UI Skeleton 骨架屏组件完全指南从基础用法到源码级防闪烁优化【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/elementSkeleton骨架屏是 Element UI 提供的加载占位组件用于在数据请求尚未返回时以接近真实界面的灰色占位结构替代空白页面显著改善用户的等待体验。本文基于 Element UI 官方文档 examples/docs/en-US/skeleton.md 展开并结合组件源码与样式实现系统讲解el-skeleton与el-skeleton-item的完整用法、属性语义、插槽机制以及throttle防闪烁的底层原理帮助你构建真正可用、性能友好且不抖动的加载骨架。为什么需要骨架屏Skeleton 的适用场景当页面加载数据时用户看到的往往是整片空白或单调的 spinner。Skeleton 组件在数据到达之前用一组灰色的矩形、圆角块与图片占位符模拟真实 DOM 的轮廓让用户预判内容结构从而获得更丰富的视觉与交互体验。Element UI 的骨架屏由两个组件协作完成el-skeleton外层容器负责loading、count、rows、animated、throttle等全局状态el-skeleton-item单个骨架单元通过variant指定矩形、圆形、按钮、图片等不同形态。两个组件在源码中分别对应 packages/skeleton/src/index.vue 与 packages/skeleton/src/item.vue并通过 packages/skeleton/index.js 以Vue.component的方式全局注册组件名分别为ElSkeleton与ElSkeletonItem。基础用法一行代码开启骨架占位最基本的骨架屏只需在模板中放置一个el-skeleton /template el-skeleton / /template不传任何属性时组件使用默认值渲染loading为true显示骨架、rows为4渲染 4 行段落、count为1只渲染一组。从 index.vue 的props定义可以看出这些默认值均由源码直接声明props: { animated: { type: Boolean, default: false }, count: { type: Number, default: 1 }, rows: { type: Number, default: 4 }, loading: { type: Boolean, default: true }, throttle: { type: Number, default: 0 } }默认渲染的 4 行段落并非等宽模板渲染逻辑index.vue会给首行添加is-first类、末行添加is-last类。对应样式 packages/theme-chalk/src/skeleton-item.scss 规定首行is-first宽度为33%末行is-last宽度为61%中间段落el-skeleton__paragraph宽度为100%。这种「首行短、末行次短、中间占满」的排布正是对真实段落文本行高参差的模拟官方文档称之为「rendering a title row with 33% width of the others」渲染一个宽度为其余行 33% 的标题行。可配置行数用 rows 控制段落数量当默认的 4 行段落不符合需求时可以通过rows属性自定义el-skeleton :rows6 /rows只在未提供template插槽时生效详见属性表其类型为 number、默认值为4。注意rows渲染的是variantp的段落单元并不影响下方将要介绍的el-skeleton-item自定义结构。加载动画用 animated 启用流光效果默认骨架块是静态的灰块而animated属性可以为其加上来回扫过的「流光」渐变动画el-skeleton :rows6 animated /当animated为true时外层容器会追加is-animated类见 index.vue。动画的真正实现位于样式 packages/theme-chalk/src/skeleton.scss通过skeleton-colormixin 生成 90 度线性渐变背景背景尺寸放大为400% 100%再利用el-skeleton-loading关键帧让背景位置从100% 50%平移到0 50%以1.4s ease无限循环background: linear-gradient( 90deg, $--skeleton-color 25%, $--skeleton-to-color 37%, $--skeleton-color 63% ); background-size: 400% 100%; animation: #{$namespace}-skeleton-loading 1.4s ease infinite;两个渐变色变量$--skeleton-color与$--skeleton-to-color定义在 packages/theme-chalk/src/common/var.scss 中可通过主题定制覆盖这意味着你可以让骨架动画颜色跟随项目主题风格。自定义模板用 template 插槽 variant 搭建真实结构Element 内置的骨架模板只覆盖最常见场景当页面结构复杂时应使用template插槽自行拼装骨架并结合不同variant的el-skeleton-item组合出最接近真实 UI 的占位结构template el-skeleton stylewidth: 240px template slottemplate el-skeleton-item variantimage stylewidth: 240px; height: 240px; / div stylepadding: 14px; el-skeleton-item variantp stylewidth: 50% / div styledisplay: flex; align-items: center; justify-items: space-between; el-skeleton-item varianttext stylemargin-right: 16px; / el-skeleton-item varianttext stylewidth: 30%; / /div /div /template /el-skeleton /template该示例用一张图片占位块240×240模拟封面图再用p与text单元模拟标题和描述行正是卡片类页面的常见骨架形态。官方文档特别强调了一个容易被忽略的实战要点自定义骨架时应尽量让结构与真实 DOM 高度一致structuring them as closer to the real DOM as possible以避免因骨架与真实内容高度差导致的 DOM 跳动DOM bouncing。variant 支持的骨架单元形态el-skeleton-item的variant属性决定了骨架单元的渲染形态取值与实现对应关系如下样式见 skeleton-item.scssvariant渲染形态默认尺寸源码实现p段落块高 16px首行 33%、末行 61%、其余 100% 宽h1大标题高$--font-size-extra-large20pxh3中标题高$--font-size-large18pxh5小标题高$--font-size-medium16pxtext文本行高$--font-size-small13px宽 100%caption说明文字高$--font-size-extra-small12pxbutton按钮块高 40px、宽 64px、圆角 4pximage图片占位宽高自适应内部渲染 SVG 占位图circle圆形占位圆角 50%尺寸对应头像中/大/小三种规格rect矩形占位默认 16px 高、100% 宽、基础圆角值得说明的是h5虽未在官方文档 API 表中列出但源码 skeleton-item.scss 已为其定义了样式属于从源码结构可以确认的扩展形态。circle的尺寸变量$--avatar-medium-size、$--avatar-large-size、$--avatar-small-size复用了 Avatar 组件的尺寸体系可推断其设计意图是模拟用户头像占位。variantimage是一个特殊实现它并非纯 CSS 灰块而是通过 item.vue 中的条件渲染挂载了 packages/skeleton/src/img-placeholder.vue 提供的内联 SVG 图片占位图一座山峰与太阳的剪影SVG 填充色为$--svg-monochrome-grey并占据单元 22% 的宽高视觉上更贴近「图片未加载」的语义。加载状态切换用 loading default 插槽呈现真实内容骨架屏的最终使命是「加载完成后展示真实 UI」。通过loading属性控制骨架 DOM 与真实 DOM 的切换真实内容放入default插槽template div stylewidth: 240px p label stylemargin-right: 16px;Switch Loading/label el-switch v-modelloading / /p el-skeleton stylewidth: 240px :loadingloading animated template slottemplate el-skeleton-item variantimage stylewidth: 240px; height: 240px; / div stylepadding: 14px; el-skeleton-item varianth3 stylewidth: 50%; / div styledisplay: flex; align-items: center; justify-items: space-between; margin-top: 16px; height: 16px; el-skeleton-item varianttext stylemargin-right: 16px; / el-skeleton-item varianttext stylewidth: 30%; / /div /div /template template el-card :body-style{ padding: 0px, marginBottom: 1px } img srchttps://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png classimage / div stylepadding: 14px; spanDelicious hamberger/span div classbottom card-header span classtime{{ currentDate }}/span el-button typetext classbuttonOperation button/el-button /div /div /el-card /template /el-skeleton /div /template script export default { data () { return { loading: true, currentDate: 2021-06-01 } }, } /script该示例中loading默认true页面先显示骨架当用户拨动开关将loading置为false组件立刻切换到default插槽中真实的卡片内容。从 index.vue 的模板结构可以看到切换的本质是v-ifuiLoading条件渲染uiLoading为真时渲染骨架容器外层el-skeletonis-animated类为假时渲染default插槽内容。列表数据渲染用 count 批量生成骨架骨架屏最常见的场景是列表加载数据未返回时先渲染多条骨架占位让页面看起来「正在逐条加载」。count属性用于控制渲染几组骨架模板template div stylewidth: 400px p el-button clicksetLoadingClick me to reload/el-button /p el-skeleton stylewidth:400px :loadingloading animated :count3 template slottemplate el-skeleton-item variantimage stylewidth: 400px; height: 267px; / div stylepadding: 14px; el-skeleton-item varianth3 stylewidth: 50%; / div styledisplay: flex; align-items: center; justify-items: space-between; margin-top: 16px; height: 16px; el-skeleton-item varianttext stylemargin-right: 16px; / el-skeleton-item varianttext stylewidth: 30%; / /div /div /template template el-card :body-style{ padding: 0px, marginBottom: 1px } v-foritem in lists :keyitem.name img :srcitem.imgUrl classimage multi-content / div stylepadding: 14px; spanDelicious hamberger/span div classbottom card-header span classtime{{ currentDate }}/span el-button typetext classbuttonOperation button/el-button /div /div /el-card /template /el-skeleton /div /template script export default { data() { return { loading: true, currentDate: 2021-06-01, lists: [], } }, mounted() { this.loading false this.lists [ { 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, }, ] }, methods: { setLoading() { this.loading true setTimeout(() (this.loading false), 2000) }, }, } /script示例中用:count3渲染 3 组相同的骨架数据到达后mounted中将loading置为false并填充 3 条真实列表数据点击按钮则通过setLoading先置true重新显示骨架再于 2 秒后切回真实列表模拟一次完整的下拉刷新。源码层面count通过 index.vue 的v-fori in count循环包裹template插槽实现多组渲染。官方文档在此处给出了明确的性能提示不建议渲染大量假 UI因为假骨架同样占用浏览器渲染资源且切换销毁时成本更高。请尽量让count保持最小以换取更好的用户体验。防闪烁优化throttle 延迟渲染的真实原理当接口响应极快时骨架刚渲染到 DOM 就要立刻切换回真实内容会造成一帧「白闪」或「闪烁」sudden flashy。throttle属性正是为此设计——它设置骨架渲染的延迟毫秒数延迟期内不渲染骨架从而平滑跳过这种瞬态template div stylewidth: 240px p label stylemargin-right: 16px;Switch Loading/label el-switch v-modelloading / /p el-skeleton stylewidth: 240px :loadingloading animated :throttle500 template slottemplate el-skeleton-item variantimage stylewidth: 240px; height: 240px; / div stylepadding: 14px; el-skeleton-item varianth3 stylewidth: 50%; / div styledisplay: flex; align-items: center; justify-items: space-between; margin-top: 16px; height: 16px; el-skeleton-item varianttext stylemargin-right: 16px; / el-skeleton-item varianttext stylewidth: 30%; / /div /div /template template el-card :body-style{ padding: 0px, marginBottom: 1px} img srchttps://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png classimage / div stylepadding: 14px; spanDelicious hamberger/span div classbottom card-header span classtime{{ currentDate }}/span el-button typetext classbuttonoperation button/el-button /div /div /el-card /template /el-skeleton /div /template script export default { data() { return { loading: false, currentDate: 2021-06-01 } }, } /script从源码看throttle并不是简单地对loading做节流而是通过内部状态uiLoading实现「进入加载态延迟生效」的策略index.vuewatch: { loading: { handler(loading) { if (this.throttle 0) { this.uiLoading loading; return; } if (loading) { clearTimeout(this.timeoutHandle); this.timeoutHandle setTimeout(() { this.uiLoading this.loading; }, this.throttle); } else { this.uiLoading loading; } }, immediate: true } }, data() { return { uiLoading: this.throttle 0 ? this.loading : false }; }其行为可以精确概括为初始状态若throttle 0uiLoading强制为false即使loading为true也暂不渲染骨架loading变为true进入加载时不立即切换而是启动setTimeout等待throttle毫秒后再把uiLoading同步为true并渲染骨架loading变为false加载结束时立即把uiLoading置为false马上展示真实内容若在延迟窗口内loading又变回falseclearTimeout会取消尚未触发的骨架渲染——这正是「接口太快时完全不闪骨架」的实现基础。因此throttle的语义是「骨架进入延迟」而非「内容显示延迟」加载结束后内容永远即时呈现只有加载过程短于throttle阈值时骨架才被跳过从而彻底规避闪烁。API 速查属性、插槽与类型定义el-skeleton 属性Skeleton Attributes属性说明类型可选值默认值animated是否显示加载动画booleantrue / falsefalsecount渲染多少组骨架模板到 DOMnumber整数1loading是否显示骨架屏booleantrue / falsetruerows段落行数仅在未提供 template 插槽时生效number整数4throttle骨架渲染延迟毫秒number整数0以上属性与 packages/skeleton/src/index.vue 的props声明一一对应同时已在 TypeScript 类型文件 types/skeleton.d.ts 中完整声明其中rows的声明类型为 boolean属于类型文件与源码实现不一致的历史遗留实际运行时按 number 处理引用时需注意。el-skeleton-item 属性Skeleton Item Attributes属性说明类型可选值默认值variant当前渲染的骨架单元类型Enum(string)p / h1 / h3 / text / caption / button / image / circle / recttext插槽Skeleton Slots插槽名说明default真实渲染的 DOM加载完成后的实际内容template自定义骨架模板加载过程中的占位结构插槽的语义同样体现在模板逻辑中当uiLoading为真时渲染template插槽未提供时退化为内置段落为假时渲染default插槽两个插槽互斥切换。类型层面可参考 types/skeleton.d.ts 中的ElSkeletonSlots接口定义。结语一套组合拳打造无跳动的加载体验综合来看Element UI 的骨架屏组件设计了一套完整的加载体验方案用variant拼装贴近真实 DOM 的自定义骨架用animated提供轻柔的加载反馈用count覆盖列表场景再用loadingdefault插槽完成骨架与真实内容的无缝切换最后以throttle从源码层面规避快速响应下的闪烁问题。遵循「骨架结构尽量贴近真实 DOM」与「count 尽量小」两条官方建议配合 packages/skeleton/src/index.vue、packages/theme-chalk/src/skeleton-item.scss 等实现细节的理解你就能在自己的项目中落地一套专业、流畅且性能友好的骨架屏方案。【免费下载链接】elementA Vue.js 2.0 UI Toolkit for Web项目地址: https://gitcode.com/gh_mirrors/eleme/element创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价