当前位置: 首页 > news >正文

ant-design-vue 库 Loading 组件封装

ant-design-vue 库中 Spin :用于页面和区块的加载中状态。页面局部处于等待异步数据或正在渲染过程时,合适的加载动效会有效缓解用户的焦虑。

重构 Loading 组件

<template>
  <section
    class="full-loading"
    v-show="loading"
  >
    <Spin v-bind="$attrs" :tip="tip" :size="size" :spinning="loading" />
  </section>
</template>
<script lang="ts">
  import { PropType } from 'vue';
  import { defineComponent } from 'vue';
  import { Spin } from 'ant-design-vue';
  import { SizeEnum } from '/@/enums/sizeEnum';

  export default defineComponent({
    name: 'Loading',
    components: { Spin },
    props: {
      tip: {
        type: String as PropType<string>,
        default: '',
      },
      size: {
        type: String as PropType<SizeEnum>,
        default: SizeEnum.LARGE,
        validator: (v: SizeEnum): boolean => {
          return [SizeEnum.DEFAULT, SizeEnum.SMALL, SizeEnum.LARGE].includes(v);
        },
      },
      loading: {
        type: Boolean as PropType<boolean>,
        default: false,
      },
    }
  })
</script>

// 对应枚举值维护
export enum SizeEnum {
  DEFAULT = 'default',
  SMALL = 'small',
  LARGE = 'large',
}

useLoading 函数

在使用 useLoadig 函数之前,调用 createLoading 创建 loading 实例。具体代码如下:

import { VNode, defineComponent, createVNode, render, reactive, h } from 'vue'
import Loading from './Loading.vue';

export function createLoading(props, target, wait = false) {
  const vm: Nullable<VNode> = null;
  const data = reactive({
    tip: '',
    loading: true,
    ...props
  })

  const LoadingWrap = defineComponent({
    render () {
      return h(Loading, { ...data })
    }
  })

  vm = createVNode(LoadingWrap)

  if (wait) {
    setTimeout(() => {
      render(vm, document.createElement('div'));
    }, 0)
  } else {
    render(vm, document.createElement('div'));
  }

  function close() {
    if (vm?.el && vm.el.parentNode) {
      vm.el.parentNode.removeChild(vm.el)
    }
  }

  function open(target: HTMLElement = document.body) {
    if (!vm || !vm.el) {
      return
    }
    target.appendChild(vm.el as HTMLElement)
  }

  if (target) {
    open(target)
  }

  return {
    vm,
    close,
    open,
    setTip: (tip: String) => {
      data.tip = tip
    },
    setLoading: (loading: boolean) => {
      data.loading = loading
    },
    get loading () {
      return data.loading
    },
    get $el() {
      return vm?.el as HTMLElement
    }
  }
}

useLoadig 函数对外提供相应的方法。

import { unref } from 'vue'
import { createLoading } from './createLoading'
import type { LoadingProps } from './typing';
import type { Ref } from 'vue';

export interface UseLoadingOptions {
  target?: any,
  props?: Partial<LoadingProps>,
}

interface Fn {
  (): void;
}

export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => void]
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void]

export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn, (string) => void] {
  let props: Partial<LoadingProps>
  let target: HTMLElement | Ref<ElRef> = document.body

  // 判断使用哪种 interface
  if (Reflect.has(opt, 'target') || Reflect.has(opt, 'props')) {
    const options = opt as Partial<UseLoadingOptions>
    props = options.props || {}
    target = options.target || document.body
  } else {
    props = opt as Partial<LoadingProps>
  }

  const instance = createLoading(props, undefined, true)

  const open = (): void => {
    const t = unref(target as Ref(ElRef))
    if (!t) return
    instance.open(t)
  }

  const close = (): void => {
    instance.close()
  }

  const setTip = (tip: string) => {
    instance.setTip(tip)
  }

  return [open, close, setTip]
}

v-loading 指令

import { createLoading } from '/@/components/Loading';

const loadingDirective: Directive = {
	mounted(el, binding) {
    const tip = el.getAttribute('loading-tip');
    const size = el.getAttribute('loading-size');
    const fullscreen = !!binding.modifiers.fullscreen
    const instance = createLoading({ tip, size: size || 'large', loading: !!bind.value }, fullscreen ? document.body : el)
    el.instance = instance
  },
  update (el, binding) {
    const instance = el.instance
    if (!instance) return
    instance.setTip(el.getAttribute('loading-tip'))
    if (binding.oldValue !== binding.value) {
      instance.setLoading?.(binding.value && !instance.loading)
    }
  },
  unmounted (el) {
    el?.instance?.close()
  }
}

export function setupLoadingDirective(app: App) {
  app.directive('loading', loadingDirective)
}

export default loadingDirective;

组件使用

对外暴露方法

import Loading from './components/Loading.vue'

export { Loading }
export { useLoading } from './components/useLoading'
export { createLoading } from './components/createLoading'

使用案例

<template>
  <div v-loading="loadingRef" loading-tip="加载中..." title="Loading组件示例"></div>
  <Loading
    :loading="loading"
    :absolute="absolute"
    :theme="theme"
    :background="background"
    :tip="tip"
  />
</template>
<script lang="ts">
  import { defineComponent, reactive, toRefs, ref } from 'vue';
  import { Loading, useLoading } from '/@/components/Loading';
  export default defineComponent({
    setup () {
	  const wrapEl = ref<ElRef>(null);
      
      const compState = reactive({
        loading: false,
        tip: '加载中...',
      })
	  // 1.组件方式
      function openLoading(absolute) {
        compState.loaded = true
        setTimeout(() => {
          compState.loaded = false
        }, 2000);
      }


      // 2. 函数方式
       const [openFullLoading, closeFullLoading] = useLoading({
        tip: '加载中...',
      });
      const [openWrapLoading, closeWrapLoading] = useLoading({
        target: wrapEl,
        props: {
          tip: '加载中...',
        },
      });
      
      // 3. 指令方式
      const loadingRef = ref(false)
      function openDirectiveLoading () {
        loadingRef.value = true
        setTimeout(() => {
          loadingRef.value = false
        }, 2000);
      }

      return {
        loadingRef,
        openDirectiveLoading,
        ...toRefs(compState),
      }
    }
  })
</script>

相关文章:

  • 2022 年前端趋势的 技术发展情况
  • Observability:使用 Elastic Agent 来收集定制的 TCP 日志
  • 【C++】C++ 入门
  • Flink Kafka Sink 源码分析
  • 高斯消元法(2)——保姆级笔记
  • R语言因子分析全流程
  • Nginx简单配置 - 基础安全
  • 基于sdrpi的openwifi实践2:生成BOOT.BIN
  • 七、安装Centos7+8系统+超级优化
  • Kali-登录暴力破解器工具-medusa使用
  • 除砷树脂HP-776
  • 矿产行业供应链协同系统解决方案:构建数智化平台,保障矿产资源安全供应
  • 申请专利流程及费用。
  • HTML+CSS期末网页设计前端作品(大三)
  • java-php-python-ssm-民航售票管理系统-计算机毕业设计
  • “大数据应用场景”之隔壁老王(连载四)
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【翻译】babel对TC39装饰器草案的实现
  • Angular6错误 Service: No provider for Renderer2
  • Centos6.8 使用rpm安装mysql5.7
  • Java比较器对数组,集合排序
  • Phpstorm怎样批量删除空行?
  • scrapy学习之路4(itemloder的使用)
  • Service Worker
  • underscore源码剖析之整体架构
  • 动态规划入门(以爬楼梯为例)
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 聊聊sentinel的DegradeSlot
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 数据结构java版之冒泡排序及优化
  • 在Unity中实现一个简单的消息管理器
  • Linux权限管理(week1_day5)--技术流ken
  • #pragma data_seg 共享数据区(转)
  • (4)事件处理——(7)简单事件(Simple events)
  • (C语言)二分查找 超详细
  • (ros//EnvironmentVariables)ros环境变量
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (九)One-Wire总线-DS18B20
  • (四)Android布局类型(线性布局LinearLayout)
  • (四)汇编语言——简单程序
  • (转)大型网站的系统架构
  • (转)重识new
  • .net core 连接数据库,通过数据库生成Modell
  • .Net Memory Profiler的使用举例
  • .NET NPOI导出Excel详解
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • /proc/vmstat 详解
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • [@Controller]4 详解@ModelAttribute
  • [Android]使用Retrofit进行网络请求
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)