封装v-loading全局自定义指令
当我们刷新页面或者是首次加载的时候, 如果后端数据请求比较慢的情况下; 页面是会出现白屏情况的
所以我们可以使用 v-loading 去优化一下, 增加用户的体验性
我们可以有两种方式去实现:
1. 定义一个 loading 的组件, 然后在每一个需要显示 loading 效果的组件中手动的导入
2. 使用自定义指令(v-loading)去动态的添加 loading 效果
显然第二种方案是要比第一种方案优雅的
如果给每一个需要添加 loading 效果的组件都去手动导入注册的话, 这个行为未免有点繁琐
首先我们需要明确, 我们需要在一个 DOM 元素中插入一个 loading 的效果
且这个 loading 效果并不是一尘不变的, 在当组件数据更新的之后; 我们需要将 loading 效果进行移除
第一步:
创建 loading.vue 公共的基础组件
此组件需要向外暴露一个 setTitle 方法, 用于动态修改 loading 的文案
-
<template>
-
<div class="loading">
-
<div class="loading-content">
-
<img width="24" height="24" src="./loading.gif">
-
<p class="desc">{{title}}</p>
-
</div>
-
</div>
-
</template>
-
-
<script>
-
export default {
-
name: 'loadingCom',
-
data () {
-
return {
-
title: ''
-
}
-
},
-
methods: {
-
setTitle (title) {
-
this.title = title
-
}
-
}
-
}
-
</script>
-
-
<style lang="scss" scoped>
-
.loading {
-
position: absolute;
-
top: 50%;
-
left: 50%;
-
transform: translate3d(-50%, -50%, 0);
-
.loading-content {
-
text-align: center;
-
.desc {
-
line-height: 20px;
-
font-size: $font-size-small;
-
color: $color-text-l;
-
}
-
}
-
}
-
</style>
第二步:
vue 提供了一个 api (directive), 用于注册自定义指令
此 api 函数中需要传入两个参数:
1. 自定义指令名称
2. 配置项
所以, 我们还需要创建 v-loading 的配置文件(directive)
然后在 main.js 入口文件中进行注册
-
// 导入v-loading的配置项
-
import loadingDirective from '@/components/base/loading/directive'
-
-
createApp(App).use(store).use(router).directive('loading', loadingDirective).mount('#app')
第三步:
现在我们就需要去封装 loadingDirective 这一个配置项
1. 首先导入 loading 组件
2. 导入 createApp 函数
3. 定义 loadingDirective 对象, 此对象中有两个成员 mounted(组件挂载时) 和 updated(组件更新时)
4. 先完成 mounted 函数的逻辑
-
import loading from './loading.vue'
-
import { createApp } from 'vue'
-
-
const loadingDirective = {
-
mounted (el, binding) {
-
// 创建根组件
-
const app = createApp(loading)
-
// 获取vue实例
-
const instance = app.mount(document.createElement('div'))
-
// 因为在添加方法内部需要使用到instance, 但是instance是一个局部变量
-
// 那么就添加到el对象中
-
el.instance = instance
-
if (binding.value) {
-
append(el)
-
}
-
}
-
}
-
-
// 添加元素方法
-
function append (el) {
-
el.appendChild(el.instance.$el)
-
}
5. 组件数据更新的时候, 需要执行 updated 钩子函数
-
import loading from './loading.vue'
-
import { createApp } from 'vue'
-
-
const loadingDirective = {
-
mounted (el, binding) {
-
const app = createApp(loading)
-
const instance = app.mount(document.createElement('div'))
-
el.instance = instance
-
if (binding.value) {
-
append(el)
-
}
-
},
-
updated (el, binding) {
-
// 如果旧值不等于新值的话, 就需要动态的执行添加和移除元素方法
-
if (binding.oldValue !== binding.value) {
-
// 组件没有再次初始化的前提下, 何时会再次显示loading效果
-
// 比如, 分页组件, 当当前页码值发生变化的时候需要重新请求数据; 渲染数据
-
binding.value ? append(el) : remove(el)
-
}
-
}
-
}
-
-
function append (el) {
-
el.appendChild(el.instance.$el)
-
}
-
-
// 移除元素方法
-
function remove (el) {
-
el.removeChild(el.instance.$el)
-
}
-
-
export default loadingDirective
配置文件写到这里, 一个 v-loading 自定义指令就完成了
但是我们还可以做一些优化:
1. 不要依赖父元素的定位, 因为 loading 效果需要在父元素的正中间显示
2. 可以动态的设置 loading 效果的文案
先来完成第一个优化
我们将对一些 dom 的操作提取到一个 dom.js 文件中
-
export function addClass (el, className) {
-
// 不要对一个元素重复添加class类
-
if (!el.classList.contains(className)) {
-
el.classList.add(className)
-
}
-
}
-
-
export function removeClass (el, className) {
-
// 如果删除一个不存在的class类是不会报错的
-
el.classList.remove(className)
-
}
然后我们需要对 loadingDirective 配置项进行修改
-
import loading from './loading.vue'
-
import { createApp } from 'vue'
-
import { addClass, removeClass } from '@/assets/js/dom'
-
-
// 在css中定义一个class类
-
const relativeCls = 'g-relative'
-
-
const loadingDirective = {
-
mounted (el, binding) {
-
const app = createApp(loading)
-
const instance = app.mount(document.createElement('div'))
-
el.instance = instance
-
if (binding.value) {
-
append(el)
-
}
-
},
-
updated (el, binding) {
-
if (binding.oldValue !== binding.value) {
-
binding.value ? append(el) : remove(el)
-
}
-
}
-
}
-
-
function append (el) {
-
// 动态的获取当前el元素的style
-
const style = getComputedStyle(el)
-
// 判断是否存在以下几种定位
-
if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
-
addClass(el, relativeCls)
-
}
-
el.appendChild(el.instance.$el)
-
}
-
-
function remove (el) {
-
// 组件数据更新的时候需要移除掉class类
-
removeClass(el, relativeCls)
-
el.removeChild(el.instance.$el)
-
}
-
-
export default loadingDirective
第二个优化:
在组件挂载的时候, 去动态的给 loading 效果添加文案
这里需要使用到 loading 组件向外暴露的 setTitle 函数
然后通过 binding.arg 方法获取传入的数据, 再调用 setTitle 进行动态的设置
-
import loading from './loading.vue'
-
import { createApp } from 'vue'
-
import { addClass, removeClass } from '@/assets/js/dom'
-
-
const relativeCls = 'g-relative'
-
-
const loadingDirective = {
-
mounted (el, binding) {
-
const app = createApp(loading)
-
const instance = app.mount(document.createElement('div'))
-
el.instance = instance
-
-
// 获取组件传入的文案数据
-
const title = binding.arg
-
if (typeof title !== 'undefined') {
-
instance.setTitle(title)
-
}
-
if (binding.value) {
-
append(el)
-
}
-
},
-
updated (el, binding) {
-
// 组件更新的时候也是一样
-
const title = binding.arg
-
if (typeof title !== 'undefined') {
-
el.instance.setTitle(title)
-
}
-
if (binding.oldValue !== binding.value) {
-
binding.value ? append(el) : remove(el)
-
}
-
}
-
}
-
-
function append (el) {
-
const style = getComputedStyle(el)
-
if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
-
addClass(el, relativeCls)
-
}
-
el.appendChild(el.instance.$el)
-
}
-
-
function remove (el) {
-
removeClass(el, relativeCls)
-
el.removeChild(el.instance.$el)
-
}
-
-
export default loadingDirective
然后看一下在需要 v-loading 的组件使用
-
<template>
-
<div v-loading:[loadingText]="loading"></div>
-
</template>
-
-
<script>
-
export default {
-
components: { Slider, Scroll },
-
data () {
-
loadingText: '正在载入...'
-
}
-
},
-
computed: {
-
loading () {
-
return // 动态判断loading效果是否显示
-
}
-
}
-
}
-
</script>
我们还可以将 loadingDirective 这一个配置项兼容不同的自定义指令, 当然前提就是共用这一个配置项的自定义指令的业务应该是差不多
首先我们得封装一个函数, 这一个函数的返回值是一个对象(也就是配置项)
然后这一个函数需要接收不同的 vue 组件, 来在 el 上生成不同的实例
所以我们需要在 update , append 和 remove 方法执行的时候, 区分 el 上不同的实例
-
import { createApp } from 'vue'
-
import { addClass, removeClass } from '@/assets/js/dom'
-
-
const relativeCls = 'g-relative'
-
-
export default function createLoadingLikeDirective (comp) {
-
return {
-
mounted (el, binding) {
-
const app = createApp(comp)
-
const instance = app.mount(document.createElement('div'))
-
-
// 区分不同的组件实例
-
const name = comp.name
-
if (!el[name]) {
-
el[name] = {}
-
}
-
el[name].instance = instance
-
-
const title = binding.arg
-
if (typeof title !== 'undefined') {
-
instance.setTitle(title)
-
}
-
-
if (binding.value) {
-
append(el)
-
}
-
},
-
updated (el, binding) {
-
const name = comp.name
-
const title = binding.arg
-
if (typeof title !== 'undefined') {
-
el[name].instance.setTitle(title)
-
}
-
-
if (binding.value !== binding.oldValue) {
-
binding.value ? append(el) : remove(el)
-
}
-
}
-
}
-
-
function append (el) {
-
const name = comp.name
-
const style = getComputedStyle(el)
-
if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
-
addClass(el, relativeCls)
-
}
-
el.appendChild(el[name].instance.$el)
-
}
-
-
function remove (el) {
-
const name = comp.name
-
removeClass(el, relativeCls)
-
el.removeChild(el[name].instance.$el)
-
}
-
}
那么 loading/directive.js 中的代码需要做修改
-
import loading from './loading.vue'
-
import createLoadingLikeDirective from '@/assets/js/create-loading-like-directive'
-
-
export default createLoadingLikeDirective(loading)
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfakgf
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01