前言
骨架屏是页面的一个空白版本,通常会在页面齐全渲染之前,通过一些灰色的区块大抵勾画出轮廓,待数据加载实现后,再替换成实在的内容。
目前支流 UI库
都有骨架屏,如 Element-UI
、Antd
能够看到应用起来非常简单,只须要一行代码即可
// element<el-skeleton />// antd<a-skeleton />
但这样不够灵便,对于谋求个性化的页面就不太行了,
起初~ 我想了想,能不能给 节点
打 标记
,而后依据 标记
主动生成对应骨架屏呢?
后果还真给我想进去了! 用 指令
先看成果 o((>< ))o ~
实现原理
咱们 自定义2个指令,别离叫做 skeleton
、skeleton-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 }
点击(在线运行)示例代码
实现原理其实很简略
- 咱们先自定义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
最初
你的点赞⭐对我十分重要,也是我保持的能源