简介
骨架屏组件Skeleton
常用于在需要等待加载内容的位置提供一个占位图形组合,可以被 Loading
完全代替,但是在可用的场景下可以提供更好的视觉效果和用户体验。
- 网络较慢,需要长时间等待加载处理的情况下。
- 图文信息内容较多的列表/卡片中。
- 只在第一次加载数据的时候使用
本文将分析其源码实现,耐心读完,相信会对您有所帮助。 组件文档 Skeleton gitee源码
更多组件剖析详见 Element 2 源码剖析组件总览 。
组件源码
组件的 prop
声明,各属性功能说明详见官方文档skeleton#attributes 。
根组件 index.vue
根组件定义了两个部分,用于加载占位和真实DOM之间的切换:
- 用于展示骨架屏,提供具名
template
插槽用于自定义占位符。 - 用于展示真实组件UI,使用匿名插槽。
组件使用了v-bind="$attrs"
实现了透传Attributes,用于父子组件的Attributes 继承。
// packages\skeleton\src\index.vue
骨架屏占位元素渲染通过内部属性uiLoading
进行控制,该属性逻辑稍后详细介绍。
属性animated
用于生成样式类is-animated
,控制占位元素是否动画效果。
.el-skeleton.is-animated .el-skeleton__item {
background: linear-gradient(90deg,#f2f2f2 25%,#e6e6e6 37%,#f2f2f2 63%);
background-size: 400% 100%;
animation: el-skeleton-loading 1.4s ease infinite
}
@keyframes el-skeleton-loading {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
占位元素渲染
默认情况下组件会渲染一条记录(属性count
默认值为1),包含四个占位元素(属性rows
默认值为4),默认为标签p
样式。
首行会被渲染一个长度 33% 的段首;多行时末行会被渲染一个长度 61% 的段尾。
.el-skeleton__p.is-last {
width: 61%;
}
.el-skeleton__p.is-first {
width: 33%;
}
渲染效果如下图:
当渲染多条数据时,若未未自定义占位元素,会出现相邻记录的段尾和段首会出现在一行中(因为使用了 标签),渲染结果如下:
当需要使用骨架屏渲染列表时,才会将 count
设置为多条。
组件生命周期&事件
组件的显示状态uiLoading
根据属性throttle
和loading
传入值动态设置。 当属性 throttle
值大于0,开启延迟渲染占位元素,侦听器会进行不同逻辑处理。
watch: {
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
};
}
当 API 的请求回来的特别快,往往骨架占位刚刚被渲染,真实的数据就已经回来了,用户的界面会突然一闪,此时为了避免这种情况,就需要通过 throttle
属性来避免这个问题。
占位元素组件 item.vue
占位元素组件el-skeleton-item
支持多种标签 p/text/h1/h3/text/caption/button/image/circle/rect
的样式。
// packages\skeleton\src\item.vue
各标签样式渲染效果如下,支持样式自定义:
当需要渲染标签image
样式时使用 svg 图标组件img-placeholder
。
// packages\skeleton\src\img-placeholder.vue
组件渲染效果如下:
样式实现
组件样式源码 packages\theme-chalk\src\skeleton.scss
使用混合指令嵌套生成组件样式。
// packages\theme-chalk\src\skeleton.scss
// mixin指令 动画效果
@mixin skeleton-color() {
// ...
animation: #{$namespace}-skeleton-loading 1.4s ease infinite;
}
// 定义 @keyframes el-skeleton-loading
@keyframes #{$namespace}-skeleton-loading {
// ...
}
// 生成 .el-skeleton
@include b(skeleton) {
// ...
// 生成 .el-skeleton__first-line,.el-skeleton__paragraph
@each $unit in (first-line, paragraph) {
@include e($unit) {
// ...
}
}
// 生成 .el-skeleton.is-animated .el-skeleton__item
@include when(animated) {
.#{$namespace}-skeleton__item {
// 混入 占位元素的动画效果
@include skeleton-color();
}
}
}
组件样式源码 packages\theme-chalk\src\skeleton-item.scss
使用混合指令嵌套生成组件样式。
// packages\theme-chalk\src\skeleton-item.scss
// mixin指令 圆形样式
@mixin circle-size($size) {
// ...
}
@include b(skeleton) {
// 生成 .el-skeleton__item
@include e(item) {
// ...
}
// 生成 .el-skeleton__circle
@include e(circle) {
border-radius: 50%;
@include circle-size($--avatar-medium-size);
// 生成 .el-skeleton__circle--lg
@include m(lg) {
@include circle-size($--avatar-large-size);
}
// 生成 .el-skeleton__circle--md
@include m(md) {
@include circle-size($--avatar-small-size);
}
}
// 生成 .el-skeleton__p
@include e(p) {
// ...
// 生成 .el-skeleton__p.is-last
@include when(last) {
width: 61%;
}
// 生成 .el-skeleton__p.is-first
@include when(first) {
width: 33%;
}
}
// 生成 .el-skeleton__[button/text/h1/h3/h5/caption]
@include e(button) {
// ...
}
// 省略 ...
// 生成 .el-skeleton__image
@include e(image) {
// ...
// 生成 .el-skeleton__image svg
svg {
// ...
}
}
}
按照样式声明,也可以使用标签 h5
样式。圆形虽然定义了不同尺寸,但是组件未提供接口。
参考&关联阅读
关注专栏
如果本文对您有所帮助请关注➕、 点赞、 收藏⭐!您的认可就是对我的最大支持!