当前位置: 首页 > news >正文

uni-app 无法实现全局 Toast?这个方法做到了!

🧑‍💻 写在开头

点赞 + 收藏 === 学会🤣🤣🤣

大家好,我是不如摸鱼去,wot-ui的主要维护者,欢迎来到我的 uni-app 分享专栏。

在 uni-app 开发中,我们经常遇到需要在任何地方(如网络请求拦截器、>路由守卫等)显示 Toast 提示的需求。然而,uni-app 的组件化架构使>得全局 Toast 的实现变得复杂。本文将介绍一套完整的解决方案,让你轻松>实现真正的全局 Toast。

本文实现的全局 Toast、Loading、MessageBox 等组件均已在现代化 uni-app 模板,基于vitesse-uni-app的深度整合 Wot UI 组件库的快速起手项目 wot-demo 中提供,模板地址在文章末尾,作者是其主要维护者。

问题分析

传统方案的局限性

在 uni-app 中,常见的 Toast 实现方式有以下几种:

  1. uni.showToast() - 功能有限,样式单一,无法自定义
  2. 传统Toast - 只能在当前组件使用,无法跨组件调用
  3. wot ui 方案 - 基于provide/inject实现,需要在 setup 顶层调用useToast,无法在路由拦截和请求拦截中使用

核心难点

  • uni-app 无法像 Vue 3 那样全局挂载组件
  • 组件实例无法在非 Vue 上下文中访问
  • 需要在网络请求和路由拦截中使用 Toast

解决方案架构

我们的解决方案包含三个核心部分:

  1. wd-toast 组件 - 基于 provide/inject 的函数式调用
  2. Layout 插件 - 实现一次插入,全局可用
  3. useGlobalToast - 基于 Pinia 的状态管理

实现详解

1. wd-toast 组件实现

wot ui 是一个当下流行的 uni-app vue3 UI 库,作者也是其重要维护者之一。

首先,我们使用 wot-ui 的 Toast 组件,它基于 provide/inject 实现函数式调用:

<!-- 在组件中使用 -->
<script setup>
const toast = useToast('myToast')// 显示 Toast
toast.show({msg: '这是一个提示',duration: 2000
})
</script><template><wd-toast selector="myToast" />
</template>

优点: 函数式调用,使用简单 缺点: useToast 必须在 setup 顶层调用,toast.show 仅能在vue组件中使用

2. Layout 插件 - 一次插入,全局可用

由于 uni-app 无法全局插入组件,我们通过 @uni-helper/vite-plugin-uni-layouts 插件实现统一布局管理:

<!-- src/layouts/default.vue -->
<template><wd-config-provider :theme-vars="themeVars" :theme="theme"><slot /><!-- 全局组件一次性插入 --><wd-notify /><wd-message-box /><wd-toast /><global-loading /><global-toast /><global-message /></wd-config-provider>
</template>

这样,所有页面都会包含这些全局组件,实现了"一次插入,全局可用"的效果。

3. GlobalToast 组件实现

<!-- src/components/GlobalToast.vue -->
<script lang="ts" setup>
const { toastOptions, currentPage } = storeToRefs(useGlobalToast())
const { close: closeGlobalToast } = useGlobalToast()const toast = useToast('globalToast')
const currentPath = getCurrentPath()// 支付宝小程序兼容性处理
// #ifdef MP-ALIPAY
const hackAlipayVisible = ref(false)
nextTick(() => {hackAlipayVisible.value = true
})
// #endif// 监听全局状态变化
watch(() => toastOptions.value, (newVal) => {if (newVal && newVal.show) {// 只在当前页面显示 Toastif (currentPage.value === currentPath) {toast.show(toastOptions.value)}}else {toast.close()}
})
</script><template><!-- 支付宝小程序特殊处理 --><!-- #ifdef MP-ALIPAY --><wd-toast v-if="hackAlipayVisible" selector="globalToast" :closed="closeGlobalToast" /><!-- #endif --><!-- #ifndef MP-ALIPAY --><wd-toast selector="globalToast" :closed="closeGlobalToast" /><!-- #endif -->
</template>

关键特性:

  • 通过 currentPage 确保 Toast 只在正确的页面显示
  • 支持支付宝小程序的兼容性处理
  • 使用 virtualHost 和 styleIsolation 优化结构和样式

4. useGlobalToast - Pinia 状态管理

// src/composables/useGlobalToast.ts
import { defineStore } from 'pinia'
import type { ToastOptions } from 'wot-design-uni/components/wd-toast/types'interface GlobalToast {toastOptions: ToastOptionscurrentPage: string
}const defaultOptions: ToastOptions = {duration: 2000,show: false,
}export const useGlobalToast = defineStore('global-toast', {state: (): GlobalToast => ({toastOptions: defaultOptions,currentPage: '',}),actions: {// 显示 Toastshow(option: ToastOptions | string) {this.currentPage = getCurrentPath()const options = CommonUtil.deepMerge(defaultOptions,typeof option === 'string' ? { msg: option } : option) as ToastOptionsthis.toastOptions = CommonUtil.deepMerge(options, {show: true,position: options.position || 'middle',}) as ToastOptions},// 成功提示success(option: ToastOptions | string) {this.show(CommonUtil.deepMerge({iconName: 'success',duration: 1500,}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)},// 错误提示error(option: ToastOptions | string) {this.show(CommonUtil.deepMerge({iconName: 'error',direction: 'vertical',}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)},// 信息提示info(option: ToastOptions | string) {this.show(CommonUtil.deepMerge({iconName: 'info',}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)},// 警告提示warning(option: ToastOptions | string) {this.show(CommonUtil.deepMerge({iconName: 'warning',}, typeof option === 'string' ? { msg: option } : option) as ToastOptions)},// 关闭 Toastclose() {this.toastOptions = defaultOptionsthis.currentPage = ''},},
})

使用方式

1. 在组件中使用

<script setup>
const globalToast = useGlobalToast()function handleClick() {globalToast.success('操作成功!')globalToast.error('操作失败!')globalToast.info('提示信息')globalToast.warning('警告信息')
}
</script>

2. 在网络请求中使用

// 请求拦截器
uni.addInterceptor('request', {fail(err) {const globalToast = useGlobalToast()globalToast.error('网络请求失败')}
})// 或在 API 封装中
export async function apiRequest(url: string, data: any) {try {const result = await uni.request({ url, data })return result}catch (error) {const globalToast = useGlobalToast()globalToast.error('请求失败,请重试')throw error}
}

3. 在路由拦截中使用

// 路由守卫
function routeGuard(to: string) {const globalToast = useGlobalToast()if (!isLogin()) {globalToast.warning('请先登录')uni.navigateTo({ url: '/pages/login/index' })return false}return true
}

扩展功能

基于同样的架构,我们还实现了:

1. GlobalLoading - 全局加载提示

export const useGlobalLoading = defineStore('global-loading', {actions: {loading(option: ToastOptions | string) {this.currentPage = getCurrentPath()this.loadingOptions = CommonUtil.deepMerge({iconName: 'loading',duration: 0,cover: true,position: 'middle',show: true,}, typeof option === 'string' ? { msg: option } : option)}}
})

2. GlobalMessage - 全局弹窗

export const useGlobalMessage = defineStore('global-message', {actions: {alert(option: GlobalMessageOptions | string) {const messageOptions = CommonUtil.deepMerge({ type: 'alert' },CommonUtil.isString(option) ? { title: option } : option)messageOptions.showCancelButton = falsethis.show(messageOptions)}}
})

总结

通过这套方案,我们成功解决了 uni-app 全局 Toast 的难题:

  1. wd-toast 组件 提供了优秀的基础能力
  2. @uni-helper/vite-plugin-uni-layouts 插件 实现了统一布局和组件的全局插入
  3. Pinia 状态管理 让我们能在任何地方调用 Toast

这个方案具有以下优势:

  • 真正的全局调用,可在任何地方使用
  • 完整的类型支持
  • 多端兼容性
  • 页面隔离机制

如果你也在为 uni-app 的全局 Toast 而烦恼,不妨试试这个方案!

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

http://icebutterfly214.com/news/45800/

相关文章:

  • 2025较好的留学机构排名前十
  • 2025年悬浮门企业综合实力排行榜TOP10:专业选购指南
  • 2025 最新集成平台公司权威推荐榜:高性价比解决方案重磅发布,含老百姓大药房合作经验与国际测评认可
  • 把docker镜像和数据迁移到新的硬盘
  • 算法数据结构之 Trie 前缀树 All In One
  • Kairoa v1.1.0 发布,跨平台桌面开发者工具
  • 题目:LeetCode 1437.是否相邻 1 都至少隔 k 个 0
  • 2025哪个留学中介做英国好
  • 2025留学机构十强西安
  • 2025出国留学机构哪个比较好一点
  • 2025成都好的留学机构有哪些
  • 2025江浙沪地区PLC控制柜制造企业综合实力榜:昆山华普拓电气蝉联技术创新与全球化服务双料标杆
  • 【LVGL】加载器部件
  • 2025年评价高的液压缸厂家最新热销排行
  • 2025年11月中国电动破胎器品牌排行榜单权威发布
  • 2025年特级防火卷帘实力厂家权威推荐:电动防火卷帘/无机布防火卷帘/钢制防火卷帘门源头厂家精选
  • RTOS概要
  • 01-playwright和selenium的区别?
  • 2025 最新数字人平台推荐!AI 数字人 / 虚拟数字人平台口碑排行榜:高性价比智能数字人制作与直播电商解决方案权威推荐
  • 2025年电子防水透气膜源头厂家权威推荐:手机防水透气膜/路灯防水透气膜/家用电器防水透气膜实力企业精选
  • 2025年知名的民事纠纷律师哪家好
  • 2025年靠谱的负压消防风机厂家推荐及采购参考
  • 2025年专业的高温粘结剂拟薄水铝石TOP实力厂家推荐榜
  • Excel处理控件Aspose.Cells教程:使用Python从Excel工作表中删除数据透视表
  • MSTest NUnit xUnit VSTest Microsoft.Testing.Platform(MTP)
  • 2025 年 11 月卷膜机厂家推荐排行榜,工业卷膜机,全自动卷膜机,RO反渗透膜卷膜机,家用卷膜机公司精选
  • 机器学习之数据集划分及验证和测试
  • MySQL EXPLAIN执行计划:SQL性能翻倍的秘密武器
  • 2025年质量好的家用沙发厂家推荐及选择参考
  • Spark SQL - Recursive CTE