uni-app 也能实现全局 Toast?这套方案彻底搞定!
在日常的 uni-app 开发中,你是否遇到过这样的场景:
- 网络请求失败时需要给用户一个全局提示;
- 路由守卫中想要提醒用户“请先登录”;
- 或者在业务逻辑深处,需要快速弹出一个 Toast。
问题是:uni-app 没有开箱即用的全局 Toast 方案,uni.showToast()
功能太简单,样式难自定义;组件化的 Toast 又只能在当前页面使用,无法跨组件调用。
本文将带你实现一套完整的全局 Toast 解决方案,并且还能扩展到全局 Loading、MessageBox —— 真正做到 随时随地调用。
一、问题分析
传统方案的局限性主要在于:
uni.showToast()
- 优点:调用方便。
- 缺点:样式固定,无法扩展。
组件化 Toast
- 优点:可定制。
- 缺点:只能在当前组件调用,没法跨页面使用。
Wot UI 的
useToast
- 优点:函数式调用简洁。
- 缺点:必须在
setup
顶层调用,无法在请求拦截器、路由守卫中使用。
核心原因在于:uni-app 不支持像 Vue3 一样挂载全局组件实例,因此无法在任意位置访问组件。
二、解决方案架构
要想解决这个问题,我们可以分三步:
- wd-toast 组件:提供基础能力;
- Layout 插件:一次性插入全局组件;
- useGlobalToast + Pinia:用状态管理来驱动 Toast,在任何地方都能调用。
三、实现步骤
1. 使用 wd-toast 组件
Wot UI 的 Toast 组件提供了函数式调用。
<script setup>
const toast = useToast('myToast')
// 显示提示
toast.show({
msg: '这是一个提示',
duration: 2000
})
</script>
<template>
<wd-toast selector="myToast" />
</template>
但这样仍有局限:必须写在 setup
顶层,无法全局调用。
2. Layout 插件:一次插入,全局可用
在 layouts/default.vue
中统一插入全局组件:
<template>
<wd-config-provider>
<slot />
<wd-toast />
<global-toast />
<global-loading />
<global-message />
</wd-config-provider>
</template>
这样所有页面都会自带这些全局组件,实现了“全局挂载”。
3. useGlobalToast + Pinia:全局状态驱动
我们用 Pinia 管理 Toast 的状态:
// useGlobalToast.ts
import { defineStore } from 'pinia'
export const useGlobalToast = defineStore('global-toast', {
state: () => ({
toastOptions: { show: false, duration: 2000 },
currentPage: '',
}),
actions: {
show(option) {
this.currentPage = getCurrentPath()
this.toastOptions = {
...this.toastOptions,
...(typeof option === 'string' ? { msg: option } : option),
show: true,
}
},
success(msg) {
this.show({ msg, iconName: 'success', duration: 1500 })
},
error(msg) {
this.show({ msg, iconName: 'error', direction: 'vertical' })
},
warning(msg) {
this.show({ msg, iconName: 'warning' })
},
close() {
this.toastOptions = { show: false, duration: 2000 }
},
},
})
全局组件 GlobalToast.vue
监听 toastOptions
的变化来显示/关闭 Toast。
<script setup>
const { toastOptions, currentPage } = storeToRefs(useGlobalToast())
const toast = useToast('globalToast')
watch(toastOptions, (opt) => {
if (opt.show && currentPage.value === getCurrentPath())
toast.show(opt)
else
toast.close()
})
</script>
<template>
<wd-toast selector="globalToast" />
</template>
四、使用示例
在组件中使用
const globalToast = useGlobalToast()
globalToast.success('操作成功!')
globalToast.error('操作失败!')
在请求拦截器中使用
uni.addInterceptor('request', {
fail() {
useGlobalToast().error('网络请求失败')
}
})
在路由守卫中使用
if (!isLogin()) {
useGlobalToast().warning('请先登录')
uni.navigateTo({ url: '/pages/login/index' })
return false
}
五、扩展功能
基于相同思路,还能实现:
- GlobalLoading:显示全局加载提示;
- GlobalMessage:全局弹窗(alert、confirm);
- Notify/MessageBox:快速拓展到更多交互场景。
六、总结
通过这套架构,我们实现了:
- 真正的全局调用:拦截器、守卫、任意逻辑中都能使用;
- 多端兼容:兼容小程序、H5、APP;
- 页面隔离:只在当前页面显示,不会出现跨页面混乱;
- 可扩展性强:同样的架构轻松扩展到 Loading、MessageBox。
如果你也在为 uni-app 全局 Toast 而烦恼,不妨试试这套方案。它不仅优雅,而且高度可扩展,让全局提示不再是难题! 🚀