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

相关推荐
世俗ˊ21 分钟前
CSS入门笔记
前端·css·笔记
子非鱼92121 分钟前
【前端】ES6:Set与Map
前端·javascript·es6
6230_26 分钟前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人35 分钟前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛1 小时前
HTML 揭秘:HTML 编码快速入门
前端·html
啥子花道1 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
麒麟而非淇淋1 小时前
AJAX 入门 day3
前端·javascript·ajax
茶茶只知道学习1 小时前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css
清汤饺子1 小时前
实践指南之网页转PDF
前端·javascript·react.js
蒟蒻的贤1 小时前
Web APIs 第二天
开发语言·前端·javascript