如何更优雅地为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,本文只是提供了一种解决问题的思路,如果有遇到其他类似场景,希望能对你有所帮助~

相关推荐
前端大卫12 分钟前
【重磅福利】学生认证可免费领取 Gemini 3 Pro 一年
前端·人工智能
孜燃23 分钟前
Flutter APP跳转Flutter APP 携带参数
前端·flutter
脾气有点小暴35 分钟前
前端页面跳转的核心区别与实战指南
开发语言·前端·javascript
lxh011341 分钟前
最长递增子序列
前端·数据结构·算法
vipbic1 小时前
我封装了一个“瑞士军刀”级插件,并顺手搞定了自动化部署
vue.js·nuxt.js
Youyzq1 小时前
前端项目发布到cdn上css被编译失效问题rgba失效和rgb失效
前端·css·算法·cdn
San30.2 小时前
深入 JavaScript 内存机制:从栈与堆到闭包的底层原理
开发语言·javascript·udp
Fantastic_sj2 小时前
Vue3相比Vue2的改进之处
前端·javascript·vue.js
vipbic2 小时前
解决npm publish的404/403和配置警告全记录
前端·npm·node.js
Bigger2 小时前
🚀 “踩坑日记”:shadcn + Vite 在 Monorepo 中配置报错
前端·react.js·vite