如何更优雅地为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 就自动有了reportSuccess,reportError 方法,且听我一一道来~

方案二探索过程

添加两个方法

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

相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A5 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄5 天前
JavaScript 隐式全局变量解析
javascript
honkun65 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19915 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net