• 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 实例。具体代码如下:

    // src/components/Loading/components/createLoading.ts
    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 函数对外提供相应的方法。

    // src/components/Loading/components/useLoading.ts
    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 指令

    // src/directives/loading.ts
    import { createLoading } from '/@/components/Loading';
    
    const loadingDirective: Directive = {
      mounted(el, binding) {
        const tip = el.getAttribut('loading-tip')
        const background = el.getAttribut('loading-background')
        const size = el.getAttribut('loading-size')
        
        const fullscreen = !!binding.modifiers.fullscreen
        const instance = createLoading({
          tip,
          background,
          size: size || 'large',
          loading: !!binding.value,
          absolute: !fullscreen,
        }, 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;
    

    全局注册使用

    // src/main.ts
    import { createApp } from 'vue'
    import { setupGlobDirectives } from '/@/directives'
    
    async function bootstrap() {
      const app = createApp()
      // 注册全局指令
      setupGlobDirectives(app)
    }
    bootstrap()
    
    // src/directives/index.ts
    // 配置注册全局指令
    import type { App } from 'vue';
    import { setupLoadingDirective } from './loading';
    
    export function setupGlobDirectives(app: App) {
      setupLoadingDirective(app);
    }
    

    组件使用

    对外暴露方法

    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>
    
  • 相关阅读:
    读书笔记:《心流》
    国货之光!ATECLOUD—功能如此强大的测试测量上位机开发工具软件!
    多线程必知必会的知识点
    一个 MySQL 隐式转换的坑,差点把服务器整崩溃了
    前端vue项目部署到云服务器教程
    软件工程经济学期末复习
    基于stm32单片机的LCD1602计时器可调时钟显示Proteus仿真
    2000-2021年上市公司M&A并购溢价计算数据(含原始数据+Stata代码)
    ElasticSearch 拼音插件elasticsearch-analysis-pinyin + IK 分词器
    java Map遍历的5种方法和一些基本使用
  • 原文地址:https://blog.csdn.net/qq_36437172/article/details/126958167