定义一个可以全局调用的 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)