前言
骨架屏是页面的一个空白版本,通常会在页面齐全渲染之前,通过一些灰色的区块大抵勾画出轮廓,待数据加载实现后,再替换成实在的内容。
目前支流 UI 库
都有骨架屏,如 Element-UI
、Antd
能够看到应用起来非常简单,只须要一行代码即可
// element
<el-skeleton />
// antd
<a-skeleton />
但这样不够灵便,对于谋求个性化的页面就不太行了,
起初~ 我想了想,能不能给 节点
打 标记
,而后依据 标记
主动生成对应骨架屏呢?
后果还真给我想进去了!用 指令
先看成果 o((>ω<))o ~
实现原理
咱们 自定义 2 个指令,别离叫做 skeleton
、skeleton-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}
点击(在线运行)示例代码
实现原理其实很简略
- 咱们先自定义 2 个指令,别离叫做
skeleton
、skeleton-item
,skeleton-item
用于标记须要被遮挡的节点,skeleton
则是管制loading
状态 - 在
SkeletonItem
的mounted
钩子函数将el
保留到list
外面 - 遍历
list
创立对应的vnode
,并且调用el.getBoundingClientRect()
获取el
的地位大小设置给vnode
,使vnode
和el
正好重叠在一个地位 - 最初调用
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
最初
你的👍点赞⭐对我十分重要,也是我保持的能源👈