前言

骨架屏是页面的一个空白版本,通常会在页面齐全渲染之前,通过一些灰色的区块大抵勾画出轮廓,待数据加载实现后,再替换成实在的内容。

目前支流 UI库 都有骨架屏,如 Element-UIAntd

能够看到应用起来非常简单,只须要一行代码即可

// element<el-skeleton />// antd<a-skeleton />

但这样不够灵便,对于谋求个性化的页面就不太行了,

起初~ 我想了想,能不能给 节点标记,而后依据 标记 主动生成对应骨架屏呢?

后果还真给我想进去了! 用 指令

先看成果 o((>< ))o ~

实现原理

咱们 自定义2个指令,别离叫做 skeletonskeleton-item

// skeleton.tsimport { reactive, watchEffect, h, render } from 'vue'const state = reactive({  // 加载状态  loading: false,  // 应用了 v-skeleton-item 指令的节点保留在这里  list: []})watchEffect(() => {  // 创立 vnode  const children = state.list.map((el) =>    h('div', {      style: {        position: 'absolute',        top: el.getBoundingClientRect().top + 'px',        left: el.getBoundingClientRect().left + 'px',        width: el.getBoundingClientRect().width + 'px',        height: el.getBoundingClientRect().height + 'px',        background: '#e5e5e5',        borderRadius: getComputedStyle(el).borderRadius,      },    })  );  // 创立 div 容器  const container = h('div', children)  // 将 div容器 渲染到 body 中  render(state.loading ? container : null, document.body)})const Skeleton = {  mounted(el, binding) {    state.loading = binding.value  },  updated(el, binding) {    state.loading = binding.value  },  unmounted(el) {    state.loading = false  }}const SkeletonItem = {  mounted(el, binding) {    // 保留 el    state.list.push(el)  },  unmounted(el) {    // 删除 el    const i = state.list.indexOf(el)    if (i == -1) return    state.list.splice(i, 1)  }}// 注册这2个指令export default {  install: app => {    app.directive('skeleton', Skeleton)    app.directive('skeleton-item', SkeletonItem)  }}export { Skeleton, SkeletonItem }

点击(在线运行)示例代码

实现原理其实很简略

  1. 咱们先自定义2个指令,别离叫做 skeletonskeleton-itemskeleton-item 用于标记须要被遮挡的节点,skeleton 则是管制 loading 状态
  2. SkeletonItemmounted 钩子函数将 el 保留到 list 外面
  3. 遍历 list 创立对应的 vnode,并且调用 el.getBoundingClientRect() 获取 el 的地位大小设置给 vnode,使 vnodeel 正好重叠在一个地位
  4. 最初调用 render(state.loading ? container : null, document.body)骨架屏 渲染进去。(state.loading 是由 skeleton 指令管制的)



咱们来看看如何应用

装置

npm i -S @x-ui-vue3/skeleton

main.js

import { createApp } from 'vue'import Skeleton from '@x-ui-vue3/skeleton'import App from './App.vue'createApp(App).use(Skeleton).mount('#app')

App.vue

<script setup>import { ref } from 'vue'const loading = ref(false)</script><template>  <label for="loading">点击切换</label>  <input v-model="loading" id="loading" type="checkbox" />  <br /><br />  <div v-skeleton="loading">    <span v-skeleton-item>超文本标记语言是一种用于创立网页的规范标记语言。</span>    <br /><br />    <span v-skeleton-item>www.runoob.com</span>    <br /><br />    <span v-skeleton-item>Good good study, day day up!</span>  </div></template>


更多示例


  • DEMO 1
  • DEMO 2
  • DEMO 3

最初

你的点赞⭐对我十分重要,也是我保持的能源