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