ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Naive UI Discrete API 完全指南:使用 createDiscreteApi 在 setup 外调用 useMessage / useDialog / useNotification

Naive UI Discrete API 完全指南:使用 createDiscreteApi 在 setup 外调用 useMessage / useDialog / useNotification 前端UI组件【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址https://gitcode.com/gh_mirrors/na/naive-ui点击查看免费下载Naive UI 从 v2.29.0 起提供了createDiscreteApi用于在组件setup之外如工具函数、路由守卫、事件回调、HTTP 拦截器等纯 TypeScript/JavaScript 环境中创建useMessage、useDialog、useNotification、useLoadingBar、useModal五类命令式 API。本文将完整讲解其签名、配置项、底层运行原理与注意事项并给出可直接复制运行的完整示例帮助你彻底告别脱离上下文的 API 调用这一痛点。为什么需要 Discrete API在 Naive UI 中useMessage、useDialog、useNotification、useLoadingBar、useModal这些 API 都是基于 Vue 的依赖注入inject实现的它们必须从组件树中最近的n-xxx-provider上读取注入值。以useMessage为例其源码use-message.ts如下export function useMessage(): MessageApiInjection { const api inject(messageApiInjectionKey, null) if (api null) { throwError( use-message, No outer n-message-provider / founded. ) } return api }也就是说只要在setup之外调用useMessageinject就取不到值并抛出 No outer provider founded 错误。useDialogcomposables.ts、useNotificationuse-notification.ts、useLoadingBaruse-loading-bar.ts、useModalcomposables.ts的实现机制完全相同。createDiscreteApi正是为解决这一问题而生的它内部创建一个独立的 Vue 应用把所需的 Provider 挂载其中再把注入出的 API 以普通对象的形式暴露出来从而让命令式 API 可以在任何地方使用。createDiscreteApi 基础用法官方演示basic.demo.vue展示了最典型的用法一次性创建五种 API并通过configProviderProps传入一个computed引用来实现主题的动态切换。script langts setup import type { ConfigProviderProps } from naive-ui import { createDiscreteApi, darkTheme, lightTheme } from naive-ui import { computed, ref } from vue const theme reflight | dark(light) const configProviderPropsRef computedConfigProviderProps(() ({ theme: theme.value light ? lightTheme : darkTheme })) const { message, notification, dialog, loadingBar, modal } createDiscreteApi( [message, dialog, notification, loadingBar, modal], { configProviderProps: configProviderPropsRef } ) function handleThemeChangeClick() { if (theme.value light) theme.value dark else theme.value light } function handleMessageTriggerClick() { message.info(Message) } function handleNotificationTriggerClick() { notification.create({ title: Notification }) } function handleDialogTriggerClick() { dialog.info({ title: Dialog }) } function handleModalTriggerClick() { modal.create({ preset: card, title: Modal }) } function handleLoadingBarTriggerClick() { loadingBar.start() setTimeout(() { loadingBar.finish() }, 1000) } /script template n-space n-button clickhandleThemeChangeClick theme: {{ theme }} /n-button n-button clickhandleMessageTriggerClick message /n-button n-button clickhandleNotificationTriggerClick notification /n-button n-button clickhandleDialogTriggerClick dialog /n-button n-button clickhandleLoadingBarTriggerClick loadingBar /n-button n-button clickhandleModalTriggerClick modal /n-button /n-space /template要点如下createDiscreteApi与darkTheme、lightTheme一样直接从naive-ui包名导入返回对象中只包含includes数组里声明的 API未声明的类型不会出现在结果中也不会被创建configProviderProps可以传普通对象也可以传Ref/computed引用——传入响应式引用时后续修改主题、locale、date-locale等配置会自动同步到离散应用中。API 签名详解createDiscreteApi的完整 TypeScript 签名如下见 index.demo-entry.md英文版见 enUS/index.demo-entry.mdfunction createDiscreteApi( includes: Arraymessage | dialog | notification | loadingBar | modal, options: { configProviderProps: RefConfigProviderProps | ConfigProviderProps messageProviderProps: RefMessageProviderProps | MessageProviderProps dialogProviderProps: RefDialogProviderProps | DialogProviderProps notificationProviderProps: RefNotificationProviderProps | NotificationProviderProps loadingBarProviderProps: RefLoadingBarProviderProps | LoadingBarProviderProps modalProviderProps: RefModalProviderProps | ModalProviderProps } ): { // 只有 includes 中包含的 API 才会被创建 message: MessageApi dialog: DialogApi notification: NotificationApi loadingBar: LoadingBarApi modal: ModalApi // Vue app app: App unmount: () void } {}参数 1includes一个字符串数组可选值为message、notification、loadingBar、dialog、modal。在 discrete.ts 的源码中includes.forEach会逐个把对应的 Provider 组件NMessageProvider、NNotificationProvider、NLoadingBarProvider、NDialogProvider、NModalProvider及对应 props 收集起来组成providersAndProps数组——这就是按需创建的实现基础。includes.forEach((type) { switch (type) { case message: providersAndProps.push({ type, Provider: NMessageProvider, props: messageProviderProps }) break // case notification / dialog / loadingBar / modal 同理 } })说明官方中文版文档中includes的类型示例未列出modal但英文版文档与源码interface.ts 中DiscreteApiType均确认支持modal且官方演示也使用了modal因此实际使用以源码与英文文档为准。参数 2options所有属性均为可选DiscreteApiOptions见 interface.ts每个 Provider 的 props 既可以是普通对象也可以是RefTMaybeRefT RefT | T属性类型说明configProviderPropsMaybeRefConfigProviderProps传递给内部NConfigProvider的 props用于配置主题theme、locale、date-locale、theme-overrides等messageProviderPropsMaybeRefMessageProviderProps传递给NMessageProvider的 props如placement、max、duration、closable等dialogProviderPropsMaybeRefDialogProviderProps传递给NDialogProvider的 propsnotificationProviderPropsMaybeRefNotificationProviderProps传递给NNotificationProvider的 propsloadingBarProviderPropsMaybeRefLoadingBarProviderProps传递给NLoadingBarProvider的 props如loadingBarStylemodalProviderPropsMaybeRefModalProviderProps传递给NModalProvider的 props返回值返回值是一个对象DiscreteApiT见 interface.ts包含message/dialog/notification/loadingBar/modal对应的命令式 API 实例仅includes中声明的类型存在app: App内部创建的独立 Vue 应用实例unmount: () void卸载离散应用并移除其 DOM 容器。运行原理一个独立的小型 Vue 应用createDiscreteApi的核心实现位于 discrete.ts它把所有 Provider 交给createDiscreteApp处理。而 discreteApp.ts 揭示了底层机制export function createDiscreteApp({ providersAndProps, configProviderProps }: DiscreteAppOptions): DiscreteApp { let app: AppElement | null createApp(App) const extractedApi: OmitDiscreteApp, unmount { app } function App(): VNode { return h(NConfigProvider, unref(configProviderProps), { default: () providersAndProps.map(({ type, Provider, props }) { return h(Provider, unref(props), { default: () h(NInjectionExtractor, { onSetup: () (extractedApi[type] injectionFactoryMap[type]()) }) }) }) }) } let hostEl: Element | null if (isBrowser) { hostEl document.createElement(div) document.body.appendChild(hostEl) app.mount(hostEl) } // ... }整个流程可以拆解为四步创建独立应用createApp(App)新建一个与主应用完全隔离的 Vue 应用实例组装组件树内部组件树为NConfigProvider → NXxxProvider → NInjectionExtractor即把用户要求的所有 Provider 嵌套挂载在NConfigProvider之下提取注入值NInjectionExtractorInjectionExtractor.tsx是一个极简组件在自身的setup阶段同步调用onSetup回调export const NInjectionExtractor defineComponent({ name: InjectionExtractor, props: { onSetup: Function as PropType() void }, setup(props, { slots }) { props.onSetup?.() return () slots.default?.() } })回调内容extractedApi[type] injectionFactoryMap[type]()正是调用useMessage()等注入函数映射表见 discreteApp.ts。由于此时组件正处于 Provider 子孙节点的setup中inject能正确取到值——这就解释了为什么 Discrete API 能脱离上下文工作挂载到独立容器在浏览器环境下代码会document.createElement(div)创建一个全新的容器节点并追加到document.body再将应用挂载进去。因此离散 API 弹出的消息、对话框等渲染在独立的 DOM 容器中与主应用的n-xxx-provider互不干扰。unmount()会依次执行app.unmount()、从body移除容器节点并将内部引用置空若重复调用会通过warn给出提示discreteApp.ts。注意事项务必阅读官方文档在 index.demo-entry.md 中特别强调了两点这是最容易踩坑的地方注意脱离上下文的 API 不会受n-xxx-provider的影响并且和应用上下文中对应组件会使用不同的 DOM 容器。如果需要的话你需要手动同步这些信息。并且最好不要混用两类 API。不要在setup中调用createDiscreteApi可能会有一些意外的问题出现。结合源码可以更深入地理解这两条告诫配置需要手动同步由于离散应用是独立的主应用中n-config-provider、n-message-provider等配置不会自动传递到离散 API。若希望两者的主题、文案、位置等保持一致必须通过options中的xxxProviderProps显式传入且应尽量与主应用的 Provider props 保持一致不要混用两类 APIuseMessage()与createDiscreteApi().message使用的是两套不同的容器与实例混用可能导致消息位置、堆叠顺序不一致等难以排查的视觉问题建议在项目中统一选择其中一种不要在setup中调用createDiscreteApi会创建应用、操作document.body属于有副作用的重操作在setup中反复执行可能引发挂载/卸载时序问题。正确做法是在模块顶层如工具函数文件或事件回调中调用一次并复用结果。实战在纯 TS 工具模块中使用下面是推荐的生产级用法——在setup之外、模块顶层一次性创建并导出 API// utils/discrete.ts import { createDiscreteApi, darkTheme, type ConfigProviderProps } from naive-ui const configProviderProps: ConfigProviderProps { theme: darkTheme } export const { message, notification, dialog, loadingBar, modal } createDiscreteApi( [message, notification, dialog, loadingBar, modal], { configProviderProps } )之后在任何工具函数、路由守卫或事件处理中直接使用// 例如在 axios 拦截器中 import { message } from /utils/discrete export function handleRequestError(error: unknown): void { message.error(请求失败请稍后重试) }若你的场景不再需要这些 API例如在测试环境中收尾可调用返回对象上的unmount()释放资源const discrete createDiscreteApi([message], {}) // ...使用后 discrete.unmount()小结createDiscreteApiv2.29.0是 Naive UI 官方提供的脱离上下文 API方案用于在setup外获取message、dialog、notification、loadingBar、modal五类命令式 API其本质是创建一个挂载在document.body独立容器上的微型 Vue 应用通过NInjectionExtractor在 Provider 的setup阶段提取注入值见 discreteApp.ts使用时要牢记两点离散 API 的配置需通过xxxProviderProps手动同步、不要与正常 Provider API 混用、不要在setup中调用相关类型DiscreteApi、DiscreteApiOptions从naive-ui包中导出见 index.ts完整的实现与演示代码可分别查阅 discrete.ts、discreteApp.ts 与 basic.demo.vue。赞分享前端UI组件【免费下载链接】naive-uiA Vue 3 Component Library. Fairly Complete. Theme Customizable. Uses TypeScript. Fast.项目地址https://gitcode.com/gh_mirrors/na/naive-ui点击查看免费下载相关推荐Naive UI 跨框架使用在 React 项目中集成 Naive UI 组件Naive UI 跨框架使用在 React 项目中集成 Naive UI 组件 Naive UI 作为一款基于 Vue 3 的高质量组件库以其丰富的组件集、前端UI组件终极指南一劳永逸解决Cursor自动更新的烦恼终极指南一劳永逸解决Cursor自动更新的烦恼 你是否曾经历过这样的场景辛辛苦苦配置好Cursor编辑器享受着流畅的AI编程体验结果第二天一打开软件自开发工具CLI本地免费生成可编辑 PPT:Presenton AI 演示文稿完整指南本地免费生成可编辑 PPT:Presenton AI 演示文稿完整指南 每次提案要交稿,先卡大纲,再卡排版。订阅制的 AI 工具,出来的第一页常常版式崩掉、还不AI 应用人工智能大模型后端前端桌面应用MCP 服务AI AgentRAG本地部署企业应用上一篇PEX项目实战指南高级应用场景与技巧解析下一篇高级技巧自定义Nemotron-TwoTower的扩散参数以获得特定输出风格创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表