Vue 3 全局通知弹窗通过模块级 reactive 队列实现状态共享,useNotification 返回统一增删清操作,配合 Teleport 渲染到 body,所有组件调用共享同一响应式数组,无需实例依赖,轻量解耦可扩展。

Vue 3 组合式函数实现全局通知弹窗的状态管理,核心在于模块级响应式状态 + 统一调度逻辑 + Teleport 渲染隔离。它不是靠组件实例维护状态,而是利用模块作用域缓存一个共享的响应式通知队列,所有调用方共用同一份数据源。
用组合式函数管理通知队列
定义一个 useNotification 组合式函数(如 composables/useNotification.ts):
- 在模块顶层创建响应式状态,不依赖任何组件:
import { ref, reactive, onUnmounted } from 'vue'
// 模块级单例:所有组件共享同一队列
const notifications = reactive
// 生成唯一 ID(可选) let idCounter = 0
export interface NotificationItem { id: number title?: string message: string type?: 'success' | 'warning' | 'error' | 'info' duration?: number // 0 表示常驻 onClose?: () => void }
export function useNotification() {
const add = (item: Omit
// 自动关闭逻辑(若 duration > 0)
if (item.duration && item.duration > 0) {
setTimeout(() => {
remove(id)
}, item.duration)
}
}
const remove = (id: number) => { const index = notifications.findIndex(n => n.id === id) if (index !== -1) { const notification = notifications[index] notification.onClose?.() notifications.splice(index, 1) } }
const clearAll = () => { notifications.forEach(n => n.onClose?.()) notifications.length = 0 }
return { notifications, // 响应式数组,可直接 v-for 渲染 add, remove, clearAll } }
> ✅ 关键点:`notifications` 是模块内 `reactive` 创建的,首次导入即初始化,后续所有组件调用 `useNotification()` 都返回对**同一个数组的引用**,状态天然全局共享。
### 在根组件中集中渲染(配合 Teleport)
创建一个全局通知容器组件 `NotificationPortal.vue`:
```vue
<template><teleport to="body"><div class="notification-portal">
<notificationitem v-for="n in notifications" :key="n.id" :notification="n"></notificationitem>
</div>
</teleport></template><script setup lang="ts">
import { useNotification } from '@/composables/useNotification'
import NotificationItem from './NotificationItem.vue'
const { notifications, remove } = useNotification()
</script><style scoped>
.notification-portal {
position: fixed;
top: 16px;
right: 16px;
z-index: 9999;
width: 320px;
max-height: 80vh;
overflow-y: auto;
}
</style>
再在 App.vue 或 main.ts 中挂载它(只需一次):
Element Plus Vue3 桌面组件库。在开发 Vue 3.x 页面时调用,使用 Element Plus 组件(如 el-button、el-input、el-form、el-table 等)。
<template><router-view></router-view><notificationportal></notificationportal></template>
全局调用: anywhere, anytime
任意业务组件中,无需引入组件、无需 v-if 控制,直接触发:
<script setup lang="ts">
import { useNotification } from '@/composables/useNotification'
const { add } = useNotification()
const handleClick = () => {
add({
message: '订单提交成功',
type: 'success',
duration: 3000,
onClose: () => console.log('已自动关闭')
})
}
</script><template><button>提交</button>
</template>
补充说明
-
不会重复初始化状态:每次调用
useNotification()只是读取和操作模块内已存在的notifications,不是新建响应式对象。 -
支持异步/动态内容:可在
add()中传入onClose、onClick等回调,或结合h()渲染自定义 vnode(如带按钮的通知)。 -
跨端兼容性好:不依赖特定 UI 库,适配 uniapp、H5、小程序(需注意
Teleport to="body"在小程序中需替换为to="#root"或使用uni.createSelectorQuery动态挂载)。 -
可扩展性强:支持添加队列优先级、去重逻辑、最大显示数量限制(如
notifications.length > 3 && notifications.shift())等。
本质上,这就是 Vue 3 “组合式函数即状态服务”的典型落地——轻量、解耦、可测试、无副作用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









