乐趣区

关于vue3:Vue-3-自定义指令-骨架屏

前言

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

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

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

// element
<el-skeleton />

// antd
<a-skeleton />

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

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

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

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

实现原理

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

// skeleton.ts
import {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

最初

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


退出移动版