1.场景
想注册一个全局的loading插件,通过方法提供显示和隐藏,如下所示:
<template>
<div v-show="isShow" class="loading">
<div class="loading-content">Loading...</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
const isShow = ref(false); //定位loading 的开关
const show = () => {
isShow.value = true;
};
const hide = function () {
isShow.value = false;
};
//对外暴露 当前组件的属性和方法
defineExpose({
isShow,
show,
hide,
});
</script>
<style scoped lang="scss">
.loading {
position: fixed;
z-index: 999;
inset: 0;
// background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
&-content {
font-size: 40px;
color: #a9a9a9;
}
}
</style>
想法是在插件里注册,也就是先渲染并插入document.body中,把暴露的方法作为应用实例的全局属性。这样在其他组件使用时,就不需要引入该组件,只需要调用全局里对应的方法即可使用。
2.注册
其实官方文档有注册方法,但是说明不清晰,没有深入测试成功。之后又照着网上其他的写法(大都是Nuxt2),都不成功,报错document is not defined。
// plugins/vue-gtag.client.js
// 官方写法,详见https://nuxt.com.cn/docs/guide/directory-structure/plugins
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID'
}
})
})
最后还是得依靠官方文档,多思考了一阵后就写出解决方案了,如下:
import { createVNode, render} from "vue";
import type { VNode, App } from "vue";
import Loading from "@/components/Loading.vue";
type Load = {
show: () => {},
hide: () => {}
}
// 声明注入属性的类型
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$loading: Load;
}
}
declare module '#app' {
interface NuxtApp {
$loading: Load;
}
}
/*
Nuxt3 注册带有浏览器元素(如document或window等)的插件所需要的条件:
1. 要在vue app实例中注册,如下所示,通过Nuxt.vueApp.use注册该插件
2. 插件文件名要携带client后缀,表示这是个只在客户端执行的插件
*/
export default defineNuxtPlugin((NuxtApp) => {
NuxtApp.vueApp.use({
async install(app: App){
const vnode: VNode = createVNode(Loading);
//render 把Vnode 生成真实DOM 并且挂载到指定节点
const element = document.createElement('div')
document.body.appendChild(element)
render(vnode, element);
// Vue 提供的全局配置 可以自定义
app.config.globalProperties.$loading = {
// isShow: vnode.component?.exposed?.isShow,
show: () => vnode.component?.exposed?.show(),
hide: () => vnode.component?.exposed?.hide(),
};
}
})
})
不需要在nuxt.config.ts里设置plugins: [{ src: '...', ssr: false}],很方便。
3.运用
当前我在某一组件里想要使用,则如下所示:
// setup环境
import { getCurrentInstance } from 'vue'
// 通过如下方式获取$loading全局变量
const $loading = getCurrentInstance()!.appContext.config.globalProperties.$loading;
// 本来以为需要process.client的条件判断,实际运行时发现没有该条件也可以
$loading.show(); // 显示
$loading.hide(); // 隐藏