如何更优雅地为naive-ui的useMessage扩展全局方法

背景

最近做了一个埋点需求,要求 message 做成统一的成功与失败上报,就是在某个功能 success 的时候,同时上报对应的成功信息,否则上报失败。

技术方案

方案一

在 项目的 utils 里面封装一个 reportMessage(reportError 同理),然后要用到的地方就用类似方法调用:

ts 复制代码
// utils
export const reportSuccess = (reportMessageArg, options) => {
  reportMessage({ ...reportMessageArg, type: 'success' });
  const message = useMessage()
  return message.success(reportMessageArg.msg, options)
}

// 其他用到的地方
import { reportSuccess } from 'utils'

reportSuccess({...})

这样确实能解决问题,但是如果你要用到 message 自带的方法,就得 import { useMessage } from 'naive-ui',如果要用到 report 上报,就得用到 import { reportSuccess, reportError } from 'utils',这样显得相当麻烦,且其他人以后有 message 上报的需求,不一定知道你写了这些方法。

方案二

因为 useMessage 返回的是对象,所以可以很好地利用对应的引用类型给其添加方法

ts 复制代码
import { useMessage } from 'naive-ui'
import { reportMessage } from '@utils'

// 给 message 添加 上报的 success 和 error 方法

export const useExtendMessage = () => {
  const message = useMessage()

  message.reportSuccess = (reportMessageArg, options) => {
    reportMessage({ ...reportMessageArg, type: 'success' })
    return message.success(reportMessageArg.msg, options)
  }

  message.reportError = (reportMessageArg, options) => {
    reportMessage({ ...reportMessageArg, type: 'error' })
    return message.error(reportMessageArg.msg, options)
  }

  return message
}

对的,你没看出,就是这么简单,但可能你会很好奇,为何这样就能全局生效?不会要我其他地方得通过import { useExtendMessage } from '@hooks'吧?,那不还是一样。。。

不是的,你还是通过import { useMessage } from 'naive-ui',但是这时候得到的 message 就自动有了reportSuccessreportError 方法,且听我一一道来~

方案二探索过程

添加两个方法

点进去 node_modules,发现 useMessage 就只是通过 inject 一个 key 来获取 message 实例:

再找到注入 messageApiInjectionKey 的地方,发现是 MessageProvider

也就是 useMessage 要起作用,必须在顶层 template 使用到 NMessageProvider(注意这句话!注意这句话!注意这句话!),类型这样:

那么可以考虑在 NMessageProvider 子组件最近的一级再架一层:

在 ExtendMessageProvider 里面 useMeesage,然后对得到的 message 实例添加方法:

这样也就实现的其他地方用到的 useMessage 得到的 message 有 reportSuccess,reportError 方法了。

全局类型提示

但是!!这时候的 useMeesage 还是没有以上两个 report 的类型:

所以还要在 global.d.ts 里面添加全局声明,而因为 MessageApiInjection 是从naive-ui/es/message/src/MessageProvider 里面 import 的:

所以我们通过declare module 'naive-ui/es/message/src/MessageProvider'加上两个类型:

这样,再通过 useMeesage 得到的 message 就有类型了:

然后我们在 Child 组件里面 log 下 message:

发现在代码层面上也成功加上了方法!!

总结

当接到类似需求时,我们当然可以像【方案一】一样,三下两除二给解决掉,但问题在于不通用(虽然代码确实能跑,哈哈 🤣),但我个人觉得,还是多花费几分钟思考一下,是否有更好的方案,是否能做的更通用些。

然后我们就利用了 vue 的 provide 与 inject,且利用了 js 对象的引用类型功能(你刚学js的时候肯定知道)实现了扩展 message 的功能。

但加上功能并不意味着完成,我们还得考虑下如何提供更好的开发体验,也就是要加上类型,使得其他地方还是通过import { useMessage } from 'naive-ui',但这时候得到的 message 既支持功能,又支持类型提示!

最后

以上只是以 naive-ui 扩展 useMessage 为例子代入,但是不局限于 naive-ui,或者 antd 之类的ui组件库,也不局限 vue 还是 react,本文只是提供了一种解决问题的思路,如果有遇到其他类似场景,希望能对你有所帮助~

相关推荐
凯小默15 分钟前
10.JavaScript 中的类和对象以及继承
javascript·继承
AI视觉网奇16 分钟前
BambuStudio 自定义加载页面
服务器·前端·javascript
橙子家21 分钟前
Extension storage、Interest groups 和 Shared storage 简介-浏览器缓存之【隐私沙盒与扩展专用存储】
前端
漂流瓶jz1 小时前
理解Webpack插件机制:从插件使用、各类编译对象、Tapable到自定义插件与钩子开发
前端·javascript·webpack
梦帮科技11 小时前
GRAVIS v4.0:基于Web的极速套利架构设计与实时数据流实现
前端·人工智能·rust·自动化·区块链·智能合约·数字货币
爱勇宝12 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
微信开发api-视频号协议12 小时前
企业微信外部群开发自动化实践过程
java·前端·微信·自动化·企业微信·ipad
甲维斯12 小时前
Fable5:20美金的顶级设计师!
前端·人工智能
kyriewen13 小时前
我同时用了 Claude Code、Cursor 和 Codex,说说三个工具的真实差距——2026 年选错工具比不用 AI 更浪费时间
前端·ai编程·claude
Jackson__13 小时前
为了拯救我的腰椎和颈椎,我做了款浏览器插件!
前端·javascript·开源