vue 扩展实例构造器

78 阅读1分钟

在项目中,是否遇到一些特殊场景,需要自定义一些全局的loading 或者 弹窗信息等。此时,扩展实例构造器就派上了用场!

在vue2中,我们通常是使用vue.extend + $mount实现; 直接贴上官网代码吧!

    <div id="mount-point"></div>
    // 创建构造器  
var Profile = Vue.extend({  
    template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',  
    data: function () {  
        return {  
            firstName: 'Walter',  
            lastName: 'White',  
            alias: 'Heisenberg'  
            }  
        }  
    })  
// 创建 Profile 实例,并挂载到一个元素上。  
new Profile().$mount('#mount-point')

结果如下:

    <p>Walter White aka Heisenberg</p>

在vue3中的实现呢? 我们查看vue3 API文档可以发现,api中已经没有extend了。取而代之的使用createApp实现。

以下实现一个简单的全局loading组件:

Loading组件实现:Loading.vue

<template>
  <div class="loader">
    <div class="loader-inner">
      <div class="loader-line-wrap">
        <div class="loader-line"></div>
      </div>
      <div class="loader-line-wrap">
        <div class="loader-line"></div>
      </div>
      <div class="loader-line-wrap">
        <div class="loader-line"></div>
      </div>
      <div class="loader-line-wrap">
        <div class="loader-line"></div>
      </div>
      <div class="loader-line-wrap">
        <div class="loader-line"></div>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
    const show = ref<boolean>(true);
    const msg = "通过instance可以读取到的变量";
    //使用了setup语法糖 需要手动暴露
    defineExpose({
      show,
      msg,
    });
</script>
<style lang="scss" scoped>
    .loader {
      background: rgba(0, 0, 0, 0.7);
      bottom: 0;
      left: 0;
      overflow: hidden;
      position: fixed;
      right: 0;
      top: 0;
      z-index: 99999;
    }

    .loader-inner {
      bottom: 0;
      height: 60px;
      left: 0;
      margin: auto;
      position: absolute;
      right: 0;
      top: 0;
      width: 100px;
    }

    .loader-line-wrap {
      animation: spin 2000ms cubic-bezier(0.175, 0.885, 0.32, 1.275) infinite;
      box-sizing: border-box;
      height: 50px;
      left: 0;
      overflow: hidden;
      position: absolute;
      top: 0;
      transform-origin: 50% 100%;
      width: 100px;
    }

    .loader-line {
      border: 4px solid transparent;
      border-radius: 100%;
      box-sizing: border-box;
      height: 100px;
      left: 0;
      margin: 0 auto;
      position: absolute;
      right: 0;
      top: 0;
      width: 100px;
    }

    .loader-line-wrap:nth-child(1) {
      animation-delay: -50ms;
    }

    .loader-line-wrap:nth-child(2) {
      animation-delay: -100ms;
    }

    .loader-line-wrap:nth-child(3) {
      animation-delay: -150ms;
    }

    .loader-line-wrap:nth-child(4) {
      animation-delay: -200ms;
    }

    .loader-line-wrap:nth-child(5) {
      animation-delay: -250ms;
    }

    .loader-line-wrap:nth-child(1) .loader-line {
      border-color: hsl(0, 80%, 60%);
      height: 90px;
      width: 90px;
      top: 7px;
    }

    .loader-line-wrap:nth-child(2) .loader-line {
      border-color: hsl(60, 80%, 60%);
      height: 76px;
      width: 76px;
      top: 14px;
    }

    .loader-line-wrap:nth-child(3) .loader-line {
      border-color: hsl(120, 80%, 60%);
      height: 62px;
      width: 62px;
      top: 21px;
    }

    .loader-line-wrap:nth-child(4) .loader-line {
      border-color: hsl(180, 80%, 60%);
      height: 48px;
      width: 48px;
      top: 28px;
    }

    .loader-line-wrap:nth-child(5) .loader-line {
      border-color: hsl(240, 80%, 60%);
      height: 34px;
      width: 34px;
      top: 35px;
    }

    @keyframes spin {

      0%,
      15% {
        transform: rotate(0);
      }

      100% {
        transform: rotate(360deg);
      }
    }
</style>

组件实例构造器实现:Loading.ts

    import { Component, createApp } from "vue";
    import Loading from './index.vue';

    let instance: any, unmount: () => void;

    function mountComponent(rootComponent: Component) {
      const app = createApp(rootComponent);
      const root = document.createElement('div');
      document.body.append(root);
      return {
        instance: app.mount(root),
        unmount: () => {
          document.body.removeChild(root)
        }
      }
    }

    // 创建一个loading组件
    export function $showLoading() {
      ({ instance, unmount } = mountComponent(Loading));
      console.log(instance);
    }

    // 销毁loading组件
    export function $hiddenLoading() {
      instance && unmount();
    }

好了,一个全局的Loading组件定义好了

Loading组件使用:index.vue

// 导入loading
import { $showLoading, $hiddenLoading } from "@/Loading.ts";
onMounted(async () => {
  // 使用loading组件
  $showLoading();
  setTimeout(() => {
    // 卸载loading组件
    $hiddenLoading();
  }, 3000);
})

注意:loading方法使用,要在生命周期函数中使用,或者准确的说是在已挂载Dom的生命周期函数中使用,否则会出现报错!!!

以上是个人理解,也有一些借鉴!如有错误,请及时指出!