关于前端:Element-2-组件源码剖析之Skeleton骨架屏

简介

骨架屏组件Skeleton 罕用于在须要期待加载内容的地位提供一个占位图形组合,能够被 Loading 齐全代替,然而在可用的场景下能够提供更好的视觉效果和用户体验。

  • 网络较慢,须要长时间期待加载解决的状况下。
  • 图文信息内容较多的列表/卡片中。
  • 只在第一次加载数据的时候应用

本文将剖析其源码实现,急躁读完,置信会对您有所帮忙。🔗 组件文档 Skeleton 🔗 gitee源码

更多组件分析详见 👉 📚 Element 2 源码分析组件总览

组件源码

组件的 prop 申明,各属性性能阐明详见官网文档skeleton#attributes 。

根组件 index.vue

根组件定义了两个局部,用于加载占位和实在DOM之间的切换:

  1. 用于展现骨架屏,提供具名template插槽用于自定义占位符。
  2. 用于展现实在组件UI,应用匿名插槽。

组件应用了v-bind="$attrs"实现了透传Attributes,用于父子组件的Attributes 继承。

// packages\skeleton\src\index.vue
<template>
  <div> 
    <template v-if="uiLoading">
      <!-- 展现骨架屏 -->
      <div :class="['el-skeleton', animated ? 'is-animated' : '', ]" v-bind="$attrs">
        <template v-for="i in count">
          <!-- 展现自定义占位符 -->
          <slot v-if="loading" name="template">
            <el-skeleton-item v-for="item in rows" variant="p"/>
          </slot>
        </template>
      </div>
    </template>
    <template v-else>
      <!-- 展现实在 UI -->
      <slot v-bind="$attrs"></slot>
    </template>
  </div>
</template>

骨架屏占位元素渲染通过外部属性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款式。

<template v-for="i in count">
  <!-- 展现自定义占位符 -->
  <slot v-if="loading" name="template">
    <el-skeleton-item
      v-for="item in rows"
      :key="item"
      :class="{
        'el-skeleton__paragraph': item !== 1,
        'is-first': item === 1,
        'is-last': item === rows && rows > 1,
      }"
      variant="p"
    />
  </slot>
</template>

首行会被渲染一个长度 33% 的段首;多行时末行会被渲染一个长度 61% 的段尾。

.el-skeleton__p.is-last {
  width: 61%;
}
.el-skeleton__p.is-first {
  width: 33%;
}

渲染成果如下图:

当渲染多条数据时,若未未自定义占位元素,会呈现相邻记录的段尾和段首会呈现在一行中(因为应用了<template> 标签),渲染后果如下:

当须要应用骨架屏渲染列表时,才会将 count 设置为多条。

组件生命周期&事件

组件的显示状态uiLoading依据属性throttleloading传入值动静设置。 当属性 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
<template>
  <div :class="['el-skeleton__item', `el-skeleton__${variant}`]">
    <img-placeholder v-if="variant === 'image'" />
  </div>
</template>

各标签款式渲染成果如下,反对款式自定义:

当须要渲染标签image款式时应用 svg 图标组件img-placeholder

// packages\skeleton\src\img-placeholder.vue
<template>
  <svg viewBox="0 0 1024 1024"  xmlns="http://www.w3.org/2000/svg">
    <path
      d="M64 896V128h896v768H64z m64-128l192-192 116.352 116.352L640 448l256 307.2V192H128v576z m224-480a96 96 0 1 1-0.064 192.064A96 96 0 0 1 352 288z"
    />
  </svg>
</template>

组件渲染成果如下:

款式实现

组件款式源码 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 款式。圆形尽管定义了不同尺寸,然而组件未提供接口。

📚参考&关联浏览

‘透传Attributes’,vuejs

关注专栏

如果本文对您有所帮忙请关注➕、 点赞👍、 珍藏⭐!您的认可就是对我的最大反对!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理