【Vue3】28-自定义插件

147 阅读1分钟

定义一个可以全局调用的 Loading 显示插件

1. 编写组件,并暴露组件的属性和方法

/src/plugins/Loading/Index.vue

<template>
<div class="load" v-if="isShow">Loading...</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

let isShow = ref<boolean>(true)

const show = () => {
    isShow.value = true
}

const hide = () => {
    isShow.value = false
}

// 可以利用 defineExpose 暴露组件的属性和方法,方便后面的全局挂载操作
defineExpose({
    isShow,
    show,
    hide
})

</script>

<style scoped>
.load {
    height: 100vh;
    background-color: #ccc;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 30px;
    font-family: 'Courier New', Courier, monospace;
}
</style>

2. 编写插件,将组件变成虚拟节点,并挂载全局变量

插件内
要么暴露一个对象,里面要有 install 方法
要么直接暴露一个 install 方法

/src/plugins/Loading/index.ts

import { App, createVNode, render, VNode } from "vue";
import Loading from './Index.vue'

export default {
    // 接收一个 全局实例对象 app,以及用户调用 app.use() 时传递的参数
    install(app: App) {
        // 将组件创建成虚拟节点
        const vNode: VNode = createVNode(Loading)
        // 渲染的时候,将这个虚拟节点挂载到 body 上
        render(vNode, document.body)
        
        // vNode?.component?.exposed 可以获取到组件内部用 defineExpose 暴露的由属性和方法组成的对象
        // console.log(vNode?.component?.exposed)

        // 将组件上的变量注册成全局变量,让全局都可用
        app.config.globalProperties.$loading = {
            show: vNode.component?.exposed?.show,
            hide: vNode.component?.exposed?.hide
        }
    }
}

3. 入口文件中 注册插件

/src/main.ts

import { createApp } from 'vue'
import App from './App.vue'
import Loading from './plugins/Loading'

const app = createApp(App)

// 调用 app.use() 注册插件
// 第一个参数就是插件的名称,后面的参数可写可不写,写了会传递到插件内部的 install 方法中
app.use(Loading)

app.mount('#app')

type Load = {
    show: () => void,
    hide: () => void,
}

// 编写 ts Loading 声明文件
declare module 'vue' {
    export interface ComponentCustomProperties {
        $loading: Load
    }
}

4. 在其它地方使用插件

/src/App.vue

<template>
</template>

<script setup lang="ts">
import { getCurrentInstance } from 'vue';
// 调用 getCurrentInstance() 获取全局实例 app
const app = getCurrentInstance()

// 调用 app 身上挂载的插件的方法
app?.proxy?.$loading.show()

setTimeout(() => {
    app?.proxy?.$loading.hide()
}, 3000)

</script>

5. 加餐:实现 app.use()

编写 myUse(自定义 use)

/src/myUse.ts

import { App } from "vue";
import { app } from './main'

interface Use {
    install: (app: App, ...options: any[]) => void
}

// 用于缓存注册了的插件,避免重复注册
const plugins = new Set()

export function myUse<T extends Use>(plugin: T, ...options: any[]) {
    if(plugins.has(plugin)){
        console.error('这个插件已经被注册!')
    } else {
        // 调用插件的 install 方法,并传递 app 实例对象 和 回传参数
        plugin.install(app, ...options)
        // 将注册过的插件添加到缓存中
        plugins.add(plugin)
    }
}

在入口文件 main.js 中更新的代码

// 要将 app 暴露供自定义的 use 使用
export const app = createApp(App)

// 引入自定义的 use 并使用
import { myUse } from './myUse'
import Loading from './plugins/Loading'
muUse(Loading)