Naive UI Message 信息提示组件完全指南:从 useMessage 到 setup 外调用
Naive UI Message 信息提示组件完全指南从 useMessage 到 setup 外调用【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址: https://gitcode.com/gh_mirrors/na/naive-ui导读Message 是 Naive UI 中最常用的轻量级反馈组件——它一般是从浏览器顶部降下来的神谕用于在页面顶部或底部短暂展示操作结果、错误警告等信息。本文以 src/message/demos/zhCN/index.demo-entry.md 为骨架结合 MessageProvider.tsx、Message.tsx、MessageEnvironment.tsx 等源码实现完整讲解 MessageProvider 的全部 Props、useMessage 注入 API、MessageOption 与 MessageReactive 的类型细节以及在 setup 外使用 Message的两种官方方案帮助你写出可投入生产的消息提示代码。使用前提组件必须处于 n-message-provider 内部Message 与普通组件不同它不通过模板标签直接渲染而是通过命令式 APIuseMessage()调用。这意味着调用useMessage的组件必须位于n-message-provider组件内部否则会抛出错误。官方文档给出如下示例!-- App.vue -- n-message-provider content / /n-message-providerimport { useMessage } from naive-ui import { defineComponent } from vue // content export default defineComponent({ setup() { const message useMessage() return { warning() { message.warning(...) } } } })从源码看这一约束是有严格保证的。use-message.ts 的实现非常直白它通过 Vue 的inject从注入链中取出messageApiInjectionKey如果取不到即外层没有n-message-provider会直接调用throwError抛出错误export function useMessage(): MessageApiInjection { const api inject(messageApiInjectionKey, null) if (api null) { throwError( use-message, No outer n-message-provider / founded. See prerequisite in ... ) } return api }而注入的源头在 MessageProvider.tsxsetup阶段通过provide(messageApiInjectionKey, api)把完整的 API 对象提供给后代组件同时provide(messageProviderInjectionKey, { props, mergedClsPrefixRef })供内部 Message 读取 Provider 的配置与主题前缀。这就是Provider 必须包裹使用方这一规则的底层原因——API 实例本身就是通过 Vue 依赖注入机制传递的。演示速览官方文档共提供 12 个演示覆盖了 Message 的核心能力演示文件讲解内容basic.demo.vue基础用法info / error / warning / success / loading 五种类型icon.demo.vue通过icon选项自定义图标如沙漏图标timing.demo.vue用duration设定持续时间如 5 秒closable.demo.vue设定closable使 Message 可点击关闭modify-content.demo.vue通过返回的 MessageReactive 实时修改内容与类型manually-close.demo.vue通过destroy()手动关闭about-theme.demo.vue主题随 Provider 联动展示期间可切换主题multiple-line.demo.vue多行文本展示placement.demo.vue六种弹出位置切换customize-message.demo.vue用render自定义渲染如用 Alert 充当 Messageno-icon.demo.vue用showIcon: false隐藏图标rtl-debug.demo.vueRTL从右到左布局调试其中值得注意的两个进阶演示将在后文展开modify-content揭示了 MessageReactive 是一个响应式对象customize-message揭示了render完全接管渲染的能力。MessageProvider Props全局配置所有消息在应用根组件挂载n-message-provider时可通过以下 Props 对所有消息进行全局默认配置名称类型默认值说明版本closablebooleanfalse所有 Message 是否显示 close 图标container-classstringundefinedMessage 容器的类名2.36.0container-stylestring \| CSSPropertiesundefinedMessage 容器的样式durationnumber3000所有 Message 默认的持续时长毫秒keep-alive-on-hoverbooleanfalse所有 Message 在悬浮时是否不销毁maxnumberundefined限制同时显示的提示信息个数placementtop \| top-left \| top-right \| bottom \| bottom-left \| bottom-righttop所有 Message 显示的位置tostring \| HTMLElementbodyMessage 容器节点的挂载位置这些 Props 的默认值可以在 message-props.ts 的messageProviderProps定义位于 MessageProvider.tsx中得到源码级印证duration默认3000placement默认topto接受string | HTMLElementcontainerStyle同时接受字符串与对象。结合源码可以更深入理解几个关键 Props 的底层行为to与 Teleport在 MessageProvider.tsx 的render中所有消息被包裹进Teleport to{this.to ?? body}。默认挂载到body因此消息能浮现在页面最顶层你也可以把它传到一个指定的 DOM 节点或选择器字符串上例如弹层内部。placement决定容器类名与对齐方式渲染时容器类名为${mergedClsPrefix}-message-container--${this.placement}同时 Message.tsx 会根据 placement 是否以top开头设置alignItems: flex-start或flex-end实现六种位置的对齐。max的淘汰策略在create内部MessageProvider.tsx当已有消息数量达到max时会先shift()移除列表头部最早的一条再 push 新消息——即新消息挤掉最旧消息的先进先出策略。closable/duration/keepAliveOnHover的逐条覆盖Provider 渲染每个MessageEnvironment时会判断单条消息是否显式传入了对应选项MessageProvider.tsx未传入的才回落到 Provider 的全局值实现全局默认 单条覆盖。另外需要说明container-class与container-style它们作用于整个消息容器即包裹所有消息的外层div见 MessageProvider.tsx可用于调整容器的层级、宽度等整体表现适合在需要精确定位容器场景下使用。useMessage 注入 API七种命令式方法useMessage()返回的 API 对象类型为MessageApiInjection即公开的MessageApi在 MessageProvider.tsx 中定义包含以下方法名称类型说明版本destroyAll() void销毁所有弹出的信息create(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建自定义类型的信息2.25.7error(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建 error 类型的信息info(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建 info 类型的信息loading(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建 loading 类型的信息success(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建 success 类型的信息warning(content: string \| (() VNodeChild), option?: MessageOption) MessageReactive创建 warning 类型的信息从源码实现看MessageProvider.tsxinfo/success/warning/error/loading都是对内部create的封装——它们在调用时自动注入对应的type字段const api: MessageApiInjection { create(content, options) { return create(content, { type: default, ...options }) }, info(content, options) { return create(content, { ...options, type: info }) }, // success / warning / error / loading 同理 destroyAll }content参数既可以是普通字符串也可以是返回VNodeChild的函数后者在 Message.tsx 中通过render(content)渲染——这为富文本/插值内容提供了可能。每个方法都返回一个MessageReactive响应式对象且所有方法都接受可选的MessageOption作为第二参数。MessageOption单条消息的配置选项调用message.info(..., option)时第二参数option的类型为MessageOption其完整定义在 types.ts名称类型说明版本closableboolean是否显示 close 图标durationnumber信息展示的时长毫秒icon() VNodeChild信息图标keepAliveOnHoverbooleanHover 到信息上是否不销毁renderMessageRenderMessage消息的渲染函数2.24.0showIconboolean是否展示图标2.25.7spinProps{ strokeWidth?: number, stroke?: string, scale?: number, radius?: number }加载图标的属性2.44.0typeinfo \| success \| warning \| error \| loading \| default信息类型默认default2.25.7onAfterLeave() void信息消失动画结束的回调onClose() void点击关闭图标的回调onLeave() void信息开始消失的回调结合源码几个选项的底层行为如下icon与内置图标映射Message.tsx 维护了一张iconRenderMap把info/success/warning/error映射到_internal/icons中的内置图标default类型则返回null无图标。createIconVNode函数Message.tsx的逻辑是若显式传入icon函数则优先使用它否则loading类型渲染NBaseLoading旋转加载图标其余类型从映射表取默认图标。loading与spinPropsloading 图标本质是NBaseLoading组件spinProps直接透传给该组件源码中默认strokeWidth{24}、scale{0.85}用户传入的属性会覆盖默认值用于微调加载图标的粗细、颜色与缩放。showIcon默认值为true见 message-props.ts设false可隐藏图标参见 no-icon.demo.vue 的用法message.warning(..., { showIcon: false })。render完全自定义渲染传入render后默认的消息外壳图标 内容 关闭按钮将不再使用而是调用renderMessage(this.$props)直接输出你的 VNode。官方演示 customize-message.demo.vue 用NAlert充当消息体并通过var(--n-box-shadow)沿用主题变量实现换个组件当 Message的效果。MessageRenderMessage 类型当使用render选项时回调参数类型定义如下type MessageRenderMessage (props: { content?: string | number | (() VNodeChild) icon?: () VNodeChild closable: boolean type: info | success | warning | error | loading onClose?: () void }) VNodeChild其源码定义在 types.ts是从MessageSetupProps中挑选出的closable | content | icon | onClose | type五个字段。也就是说自定义渲染函数可以拿到单条消息的内容、类型、是否可关闭与关闭回调方便你把这些信息映射到任意组件上如NAlert的type、closable、onClose、default插槽。MessageReactive可实时修改的响应式消息create等方法的返回值是MessageReactive——它是一个Vue reactive 响应式对象因此可以在消息存活期间动态修改其属性界面会随之更新。官方文档定义的属性与方法如下MessageReactive Properties名称类型说明版本closableboolean是否显示 close 图标contentstring \| (() VNodeChild)信息内容destroy() void销毁信息的方法icon() VNodeChild信息图标keepAliveOnHoverbooleanHover 到信息上是否不销毁showIconboolean是否展示图标2.25.7typeinfo \| success \| warning \| error \| loading \| default信息类型默认default2.25.7onAfterLeave() void信息消失动画结束的回调onLeave() void信息开始消失的回调MessageReactive Methods名称类型说明destroy()销毁信息的方法源码层面的关键点MessageProvider.tsx每条消息在创建时会被赋予一个通过createId()生成的唯一key存入messageListRef响应式数组返回给调用方的messageReactive由reactive({ ...options, content, key, destroy })构造其中destroy的实现是找到该 key 对应的MessageEnvironment实例并调用其hide()方法随后由handleAfterLeave在离开动画结束后把它从列表中移除因为对象本身是响应式的修改msgReactive.content、msgReactive.type等字段会实时反映到界面上——这正是 modify-content.demo.vue 中加一 / 改变类型按钮背后的机制。手动关闭的两种方式通过返回的 MessageReactiveconst msg message.info(...); msg.destroy()。官方演示 manually-close.demo.vue 还展示了两个实用细节设置duration: 0让消息不自动消失以及组件卸载时在onBeforeUnmount中调用removeMessage()清理残留消息避免内存泄漏。通过destroyAll()一次性销毁所有弹出的消息适合在路由切换或登出等场景下清理界面。位置、时长与悬浮Message 的三大运行机制六种弹出位置通过placement可在top、top-left、top-right、bottom、bottom-left、bottom-right之间选择placement.demo.vue 演示了通过n-message-provider :placementplacement动态切换。这一机制由容器类名 Flex 对齐共同实现容器使用不同的--{placement}修饰类定位消息组而 Message.tsx 根据是否top开头设置 wrapper 的对齐方式从而把单条消息锚定在容器内对应方位。时长与定时器MessageEnvironmentMessageEnvironment.tsx在onMounted后调用setHideTimeout()用window.setTimeout(hide, duration)实现自动消失若duration为 0 或假值则不会设置定时器这就是手动关闭演示中duration: 0能让消息常驻的原因。Hover 保持keepAliveOnHover当开启keepAliveOnHover时MessageEnvironment.tsx 会给消息挂上mouseenter/mouseleave监听进入时clearTimeout暂停倒计时离开时重新setHideTimeout()恢复倒计时。注意源码中if (e.currentTarget ! e.target) return的守卫意味着该行为只对直接悬浮在消息本体上的事件生效。未开启该选项时MessageEnvironment.tsx 不绑定任何悬浮监听duration一到即消失。同时Message的隐藏/显示被包裹在NFadeInExpandTransition过渡动画中MessageEnvironment.tsxonLeave/onAfterLeave回调分别对应动画开始与结束的时刻。主题联动说明Message 的主题遵循就近继承原则如果你不明确指明主题被创建信息的主题会与对应n-message-provider的主题一致参见 about-theme.demo.vue该演示允许在消息展示期间切换主题。实现上Message.tsx 通过inject(messageProviderInjectionKey)拿到 Provider 的props再调用useTheme(Message, ...)解析主题变量并通过cssVarsRef输出为--n-*系列 CSS 变量如--n-color、--n-box-shadow、--n-text-color、--n-border-radius等见 Message.tsx。这意味着 Message 支持完整的主题定制既可用n-config-providertheme-overrides定制也支持inline-theme-disabled模式下的主题类名方案源码中useThemeClass(message, computed(() props.type[0]), ...)即为该模式服务。内置亮色/暗色主题定义位于 src/message/styleslight.ts/dark.ts每种类型info/success/warning/error/loading都有独立的文字色、背景色、阴影与图标色变量。Q A在 setup 外使用 MessageuseMessage依赖 Vue 的provide/inject因此无法直接在普通工具函数如 axios 拦截器、路由守卫中调用。官方文档提供了两种解决方案。选择 1使用 createDiscreteApi使用 createDiscreteApi 创建一个离散式的 API 实例绕过组件树的注入依赖。DiscreteApiOptions中messageProviderProps字段src/discrete/src/interface.ts接收MaybeRefMessageProviderProps即可以传入n-message-provider支持的全部 Props 作为初始配置。基本用法import { createDiscreteApi } from naive-ui const { message } createDiscreteApi([message], { messageProviderProps: { placement: top-right } }) // 在任何地方调用 message.success(操作成功)官方文档特别提醒使用 createDiscreteApi 前请认真阅读它的注意事项并且最好不要把createDiscreteApi和useMessage在同一 App 中混用——因为二者创建的是两套相互独立的消息实例与容器混用可能导致行为不一致例如两套容器位置、主题、zIndex 互相干扰。选择 2把 message 挂载到 window如果你只想在个别工具函数里使用可以走顶层 setup 预挂载方案在应用入口组件位于n-message-provider内部的setup中把useMessage()的返回值挂到window上之后在任意 JS 文件中直接使用。调用前需要确保 message 已经挂载成功即顶层组件已完成 setup。!-- App.vue -- n-message-provider content / /n-message-provider!-- content.vue -- template.../template script import { useMessage } from naive-ui import { defineComponent } from vue // content export default defineComponent({ setup() { window.$message useMessage() } }) /script// xxx.js export function handler() { // 需要确保已经在 setup 中执行了 window.$message message window.$message.success( Cause you walked hand in hand With another man in my place ) }两种方案各有适用场景选择 1适合需要独立、可控生命周期且要在大量非组件模块中使用的场景注意与useMessage混用的警告选择 2实现最简单但依赖挂载时序且会引入全局变量适合小型项目中少量工具函数的临时调用。结语与进一步阅读Message 的完整使用链路是n-message-providerTeleport 到 body 的容器 全局默认配置→useMessage()从注入链获取命令式 API→ 方法调用返回响应式MessageReactive→MessageEnvironment定时器与悬浮控制→Message主题变量与图标渲染。掌握 Provider 全局 Props、Option 单条覆盖、Reactive 实时修改与 setup 外调用方案即可在任何业务场景中优雅地使用消息反馈。进一步深入可参考仓库中的以下资源组件入口与类型导出src/message/index.tsNMessageProvider、useMessage、MessageApi、MessageReactive等完整源码MessageProvider.tsx、Message.tsx、MessageEnvironment.tsx、message-props.ts、types.ts全部演示src/message/demos/zhCN12 个 demo 对应本文各小节单元测试src/message/tests/Message.spec.tsx含交互与动画相关断言、src/message/tests/server.spec.tsxSSR 场景主题变量src/message/styles/light.ts、src/message/styles/dark.ts、src/message/styles/_common.ts【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址: https://gitcode.com/gh_mirrors/na/naive-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考