• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

封装v-loading全局自定义指令

武飞扬头像
hmxs_hmbb
帮助1

当我们刷新页面或者是首次加载的时候, 如果后端数据请求比较慢的情况下; 页面是会出现白屏情况的

所以我们可以使用 v-loading 去优化一下, 增加用户的体验性


我们可以有两种方式去实现:

1. 定义一个 loading 的组件, 然后在每一个需要显示 loading 效果的组件中手动的导入

2. 使用自定义指令(v-loading)去动态的添加 loading 效果

显然第二种方案是要比第一种方案优雅的

如果给每一个需要添加 loading 效果的组件都去手动导入注册的话, 这个行为未免有点繁琐

首先我们需要明确, 我们需要在一个 DOM 元素中插入一个 loading 的效果

且这个 loading 效果并不是一尘不变的, 在当组件数据更新的之后; 我们需要将 loading 效果进行移除


第一步: 

创建 loading.vue 公共的基础组件

此组件需要向外暴露一个 setTitle 方法, 用于动态修改 loading 的文案

  1.  
    <template>
  2.  
    <div class="loading">
  3.  
    <div class="loading-content">
  4.  
    <img width="24" height="24" src="./loading.gif">
  5.  
    <p class="desc">{{title}}</p>
  6.  
    </div>
  7.  
    </div>
  8.  
    </template>
  9.  
     
  10.  
    <script>
  11.  
    export default {
  12.  
    name: 'loadingCom',
  13.  
    data () {
  14.  
    return {
  15.  
    title: ''
  16.  
    }
  17.  
    },
  18.  
    methods: {
  19.  
    setTitle (title) {
  20.  
    this.title = title
  21.  
    }
  22.  
    }
  23.  
    }
  24.  
    </script>
  25.  
     
  26.  
    <style lang="scss" scoped>
  27.  
    .loading {
  28.  
    position: absolute;
  29.  
    top: 50%;
  30.  
    left: 50%;
  31.  
    transform: translate3d(-50%, -50%, 0);
  32.  
    .loading-content {
  33.  
    text-align: center;
  34.  
    .desc {
  35.  
    line-height: 20px;
  36.  
    font-size: $font-size-small;
  37.  
    color: $color-text-l;
  38.  
    }
  39.  
    }
  40.  
    }
  41.  
    </style>
学新通

第二步:

vue 提供了一个 api (directive), 用于注册自定义指令

此 api 函数中需要传入两个参数:

1. 自定义指令名称

2. 配置项

所以, 我们还需要创建 v-loading 的配置文件(directive)

然后在 main.js 入口文件中进行注册

  1.  
    // 导入v-loading的配置项
  2.  
    import loadingDirective from '@/components/base/loading/directive'
  3.  
     
  4.  
    createApp(App).use(store).use(router).directive('loading', loadingDirective).mount('#app')

第三步:

现在我们就需要去封装 loadingDirective 这一个配置项 

1. 首先导入 loading 组件

2. 导入 createApp 函数

3. 定义 loadingDirective 对象, 此对象中有两个成员 mounted(组件挂载时) 和 updated(组件更新时)

4. 先完成 mounted 函数的逻辑

  1.  
    import loading from './loading.vue'
  2.  
    import { createApp } from 'vue'
  3.  
     
  4.  
    const loadingDirective = {
  5.  
    mounted (el, binding) {
  6.  
    // 创建根组件
  7.  
    const app = createApp(loading)
  8.  
    // 获取vue实例
  9.  
    const instance = app.mount(document.createElement('div'))
  10.  
    // 因为在添加方法内部需要使用到instance, 但是instance是一个局部变量
  11.  
    // 那么就添加到el对象中
  12.  
    el.instance = instance
  13.  
    if (binding.value) {
  14.  
    append(el)
  15.  
    }
  16.  
    }
  17.  
    }
  18.  
     
  19.  
    // 添加元素方法
  20.  
    function append (el) {
  21.  
    el.appendChild(el.instance.$el)
  22.  
    }
学新通

5. 组件数据更新的时候, 需要执行 updated 钩子函数

  1.  
    import loading from './loading.vue'
  2.  
    import { createApp } from 'vue'
  3.  
     
  4.  
    const loadingDirective = {
  5.  
    mounted (el, binding) {
  6.  
    const app = createApp(loading)
  7.  
    const instance = app.mount(document.createElement('div'))
  8.  
    el.instance = instance
  9.  
    if (binding.value) {
  10.  
    append(el)
  11.  
    }
  12.  
    },
  13.  
    updated (el, binding) {
  14.  
    // 如果旧值不等于新值的话, 就需要动态的执行添加和移除元素方法
  15.  
    if (binding.oldValue !== binding.value) {
  16.  
    // 组件没有再次初始化的前提下, 何时会再次显示loading效果
  17.  
    // 比如, 分页组件, 当当前页码值发生变化的时候需要重新请求数据; 渲染数据
  18.  
    binding.value ? append(el) : remove(el)
  19.  
    }
  20.  
    }
  21.  
    }
  22.  
     
  23.  
    function append (el) {
  24.  
    el.appendChild(el.instance.$el)
  25.  
    }
  26.  
     
  27.  
    // 移除元素方法
  28.  
    function remove (el) {
  29.  
    el.removeChild(el.instance.$el)
  30.  
    }
  31.  
     
  32.  
    export default loadingDirective
学新通

配置文件写到这里, 一个 v-loading 自定义指令就完成了

但是我们还可以做一些优化:

1. 不要依赖父元素的定位, 因为 loading 效果需要在父元素的正中间显示

2. 可以动态的设置 loading 效果的文案


先来完成第一个优化

我们将对一些 dom 的操作提取到一个 dom.js 文件中

  1.  
    export function addClass (el, className) {
  2.  
    // 不要对一个元素重复添加class类
  3.  
    if (!el.classList.contains(className)) {
  4.  
    el.classList.add(className)
  5.  
    }
  6.  
    }
  7.  
     
  8.  
    export function removeClass (el, className) {
  9.  
    // 如果删除一个不存在的class类是不会报错的
  10.  
    el.classList.remove(className)
  11.  
    }

然后我们需要对 loadingDirective 配置项进行修改

  1.  
    import loading from './loading.vue'
  2.  
    import { createApp } from 'vue'
  3.  
    import { addClass, removeClass } from '@/assets/js/dom'
  4.  
     
  5.  
    // 在css中定义一个class类
  6.  
    const relativeCls = 'g-relative'
  7.  
     
  8.  
    const loadingDirective = {
  9.  
    mounted (el, binding) {
  10.  
    const app = createApp(loading)
  11.  
    const instance = app.mount(document.createElement('div'))
  12.  
    el.instance = instance
  13.  
    if (binding.value) {
  14.  
    append(el)
  15.  
    }
  16.  
    },
  17.  
    updated (el, binding) {
  18.  
    if (binding.oldValue !== binding.value) {
  19.  
    binding.value ? append(el) : remove(el)
  20.  
    }
  21.  
    }
  22.  
    }
  23.  
     
  24.  
    function append (el) {
  25.  
    // 动态的获取当前el元素的style
  26.  
    const style = getComputedStyle(el)
  27.  
    // 判断是否存在以下几种定位
  28.  
    if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
  29.  
    addClass(el, relativeCls)
  30.  
    }
  31.  
    el.appendChild(el.instance.$el)
  32.  
    }
  33.  
     
  34.  
    function remove (el) {
  35.  
    // 组件数据更新的时候需要移除掉class类
  36.  
    removeClass(el, relativeCls)
  37.  
    el.removeChild(el.instance.$el)
  38.  
    }
  39.  
     
  40.  
    export default loadingDirective
学新通

第二个优化:

在组件挂载的时候, 去动态的给 loading 效果添加文案

这里需要使用到 loading 组件向外暴露的 setTitle 函数

然后通过 binding.arg 方法获取传入的数据, 再调用 setTitle 进行动态的设置

  1.  
    import loading from './loading.vue'
  2.  
    import { createApp } from 'vue'
  3.  
    import { addClass, removeClass } from '@/assets/js/dom'
  4.  
     
  5.  
    const relativeCls = 'g-relative'
  6.  
     
  7.  
    const loadingDirective = {
  8.  
    mounted (el, binding) {
  9.  
    const app = createApp(loading)
  10.  
    const instance = app.mount(document.createElement('div'))
  11.  
    el.instance = instance
  12.  
     
  13.  
    // 获取组件传入的文案数据
  14.  
    const title = binding.arg
  15.  
    if (typeof title !== 'undefined') {
  16.  
    instance.setTitle(title)
  17.  
    }
  18.  
    if (binding.value) {
  19.  
    append(el)
  20.  
    }
  21.  
    },
  22.  
    updated (el, binding) {
  23.  
    // 组件更新的时候也是一样
  24.  
    const title = binding.arg
  25.  
    if (typeof title !== 'undefined') {
  26.  
    el.instance.setTitle(title)
  27.  
    }
  28.  
    if (binding.oldValue !== binding.value) {
  29.  
    binding.value ? append(el) : remove(el)
  30.  
    }
  31.  
    }
  32.  
    }
  33.  
     
  34.  
    function append (el) {
  35.  
    const style = getComputedStyle(el)
  36.  
    if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
  37.  
    addClass(el, relativeCls)
  38.  
    }
  39.  
    el.appendChild(el.instance.$el)
  40.  
    }
  41.  
     
  42.  
    function remove (el) {
  43.  
    removeClass(el, relativeCls)
  44.  
    el.removeChild(el.instance.$el)
  45.  
    }
  46.  
     
  47.  
    export default loadingDirective
学新通

学新通

然后看一下在需要 v-loading 的组件使用

  1.  
    <template>
  2.  
    <div v-loading:[loadingText]="loading"></div>
  3.  
    </template>
  4.  
     
  5.  
    <script>
  6.  
    export default {
  7.  
    components: { Slider, Scroll },
  8.  
    data () {
  9.  
    loadingText: '正在载入...'
  10.  
    }
  11.  
    },
  12.  
    computed: {
  13.  
    loading () {
  14.  
    return // 动态判断loading效果是否显示
  15.  
    }
  16.  
    }
  17.  
    }
  18.  
    </script>
学新通

我们还可以将 loadingDirective 这一个配置项兼容不同的自定义指令, 当然前提就是共用这一个配置项的自定义指令的业务应该是差不多

首先我们得封装一个函数, 这一个函数的返回值是一个对象(也就是配置项)

然后这一个函数需要接收不同的 vue 组件, 来在 el 上生成不同的实例

所以我们需要在 update , append 和 remove 方法执行的时候, 区分 el 上不同的实例

  1.  
    import { createApp } from 'vue'
  2.  
    import { addClass, removeClass } from '@/assets/js/dom'
  3.  
     
  4.  
    const relativeCls = 'g-relative'
  5.  
     
  6.  
    export default function createLoadingLikeDirective (comp) {
  7.  
    return {
  8.  
    mounted (el, binding) {
  9.  
    const app = createApp(comp)
  10.  
    const instance = app.mount(document.createElement('div'))
  11.  
     
  12.  
    // 区分不同的组件实例
  13.  
    const name = comp.name
  14.  
    if (!el[name]) {
  15.  
    el[name] = {}
  16.  
    }
  17.  
    el[name].instance = instance
  18.  
     
  19.  
    const title = binding.arg
  20.  
    if (typeof title !== 'undefined') {
  21.  
    instance.setTitle(title)
  22.  
    }
  23.  
     
  24.  
    if (binding.value) {
  25.  
    append(el)
  26.  
    }
  27.  
    },
  28.  
    updated (el, binding) {
  29.  
    const name = comp.name
  30.  
    const title = binding.arg
  31.  
    if (typeof title !== 'undefined') {
  32.  
    el[name].instance.setTitle(title)
  33.  
    }
  34.  
     
  35.  
    if (binding.value !== binding.oldValue) {
  36.  
    binding.value ? append(el) : remove(el)
  37.  
    }
  38.  
    }
  39.  
    }
  40.  
     
  41.  
    function append (el) {
  42.  
    const name = comp.name
  43.  
    const style = getComputedStyle(el)
  44.  
    if (['fixed', 'absolute', 'relative'].indexOf(style.position) === -1) {
  45.  
    addClass(el, relativeCls)
  46.  
    }
  47.  
    el.appendChild(el[name].instance.$el)
  48.  
    }
  49.  
     
  50.  
    function remove (el) {
  51.  
    const name = comp.name
  52.  
    removeClass(el, relativeCls)
  53.  
    el.removeChild(el[name].instance.$el)
  54.  
    }
  55.  
    }
学新通

那么 loading/directive.js 中的代码需要做修改

  1.  
    import loading from './loading.vue'
  2.  
    import createLoadingLikeDirective from '@/assets/js/create-loading-like-directive'
  3.  
     
  4.  
    export default createLoadingLikeDirective(loading)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfakgf
系列文章
更多 icon
同类精品
更多 icon
继续加载