尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

airi 项目中的 VueUse useConfirmDialog 实战:用事件钩子与 Promise 优雅实现确认弹窗链

airi 项目中的 VueUse useConfirmDialog 实战:用事件钩子与 Promise 优雅实现确认弹窗链 airi 项目中的 VueUse useConfirmDialog 实战用事件钩子与 Promise 优雅实现确认弹窗链【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi本篇技术指南围绕 VueUse 的useConfirmDialogcomposable 展开讲解如何以极简的代码支撑起模态框、确认对话框乃至多级确认链路的完整业务逻辑。在 airi 这类基于 Vue 3 构建的 AI 对话舞台项目中角色删除、会话切换、模型参数重置等危险操作都需要可靠的二次确认useConfirmDialog恰好能以「事件钩子 Promise 双模式」把确认交互的骨架一次性搭好。读完本文你将掌握reveal/confirm/cancel三个核心方法与onReveal/onConfirm/onCancel三个事件钩子的协作机制理解其底层createEventHook实现原理并能直接落地到自己的 Vue 组件中。useConfirmDialog 是什么useConfirmDialog是 VueUse 提供的一个 Utilities 类 composable其定位在 SKILL.md 中被描述为Creates event hooks to support modals and confirmation dialog chains.它本身不负责渲染任何 UI而是为「模态框 / 需要用户确认的操作」提供一套业务逻辑骨架函数可以直接用在模板上而事件钩子则充当模态框确认逻辑的挂载点。这种设计把「弹窗显示状态」与「确认/取消后的副作用」彻底解耦——视图层只关心isRevealed业务层只关心三个 hook 回调。该文档收录于仓库的 useConfirmDialog.md是 airi 仓库内置的 VueUse 函数参考体系.agents/skills/vueuse-functions/references/中的一员标注的调用策略Invocation为AUTO即“只要适用就应优先使用”。核心 API三个方法与三个钩子useConfirmDialog()无参调用即可返回一整套控制句柄它们的分工如下成员类型作用isRevealedComputedRefboolean弹窗可见状态模板中用v-if/v-show驱动显隐reveal(data?)函数触发onReveal钩子并把isRevealed.value置为true同时返回一个 Promise由后续的confirm()或cancel()来 resolveconfirm(data?)函数把isRevealed.value置为false并触发onConfirm钩子resolve 掉reveal()返回的 Promisecancel(data?)函数把isRevealed.value置为false并触发onCancel钩子resolve 掉reveal()返回的 PromiseonRevealEventHookOnRevealData弹窗创建/展示前触发的钩子可在此做初始化onConfirmEventHookOnConfirmData点击「确认」时触发能拿到confirm()传入的数据onCancelEventHookOnCancelData点击「取消」时触发能拿到cancel()传入的数据从类型签名可以看到它的三个泛型参数RevealData、ConfirmData、CancelData默认都是any分别约束三段数据的类型这也是它区别于普通布尔开关的关键——每一次确认操作都可以携带上下文数据。export declare function useConfirmDialog RevealData any, ConfirmData any, CancelData any, ( revealed?: ShallowRefboolean, ): UseConfirmDialogReturnRevealData, ConfirmData, CancelData注意第一个参数revealed?: ShallowRefboolean你可以传入一个外部已有的shallowRef来接管显示状态例如与 Pinia store 中的状态共享不传则内部自行创建。底层机制createEventHook三个on*钩子的底层实现来自 VueUse 的createEventHook。它提供了一个迷你事件总线on(fn)注册回调返回{ off }用于解绑trigger(...param)触发所有已注册回调off(fn)/clear()用于移除单个或全部回调。useConfirmDialog内部正是用三个createEventHook实例分别承载onReveal、onConfirm、onCancel调用confirm(data)时执行onConfirmHook.trigger(data)从而把数据广播给所有注册者。这也解释了为什么钩子回调可以注册多次、并且能拿到函数传入的数据对象。用法一事件钩子模式Hooks适合「确认后的副作用需要集中处理、且 UI 与逻辑分层清晰」的场景。直接解构出全部成员模板用isRevealed控制弹窗按钮绑定reveal/confirm/cancelscript setup langts import { useConfirmDialog } from vueuse/core const { isRevealed, reveal, confirm, cancel, onReveal, onConfirm, onCancel } useConfirmDialog() onReveal(() { // 弹窗展示前可在这里填充数据、重置表单 }) onConfirm(() { // 用户点了「确认」执行真正的删除/提交逻辑 }) onCancel(() { // 用户点了「取消」清理现场或记录埋点 }) /script template button clickreveal Reveal Modal /button teleport tobody div v-ifisRevealed classmodal-bg div classmodal h2Confirm?/h2 button clickconfirm Yes /button button clickcancel Cancel /button /div /div /teleport /template这里把弹窗通过teleport tobody挂到 body 下避免被父级overflow/transform等样式裁剪配合v-ifisRevealed在关闭时直接从 DOM 移除天然实现了销毁式弹窗。建议配合项目实践airi 的舞台 UI 体系中弹窗往往结合reka-ui的DialogRoot组件与defineModel的v-model:open受控模式使用参考 sessions-dialog.vue 与 sessions-drawer.vue。useConfirmDialog与这类受控弹窗的衔接方式通常是模板层由isRevealed驱动钩子内执行业务副作用两者互不干扰。用法二Promise 模式如果你更习惯「命令式」的写法——在业务逻辑里await用户的抉择结果Promise 模式更合适。核心是reveal()返回的 Promise 会 resolve 出一个结果对象通过isCanceled区分确认与取消script setup langts import { useConfirmDialog } from vueuse/core const { isRevealed, reveal, confirm, cancel, } useConfirmDialog() async function openDialog() { const { data, isCanceled } await reveal() if (!isCanceled) console.log(data) } /script template button clickopenDialog Show Modal /button teleport tobody div v-ifisRevealed classmodal-layout div classmodal h2Confirm?/h2 button clickconfirm(true) Yes /button button clickconfirm(false) No /button /div /div /teleport /template在这个例子里模板上的两个按钮分别执行confirm(true)与confirm(false)——confirm可以携带任意数据而 await 端拿到的data就是该数据。这意味着确认结果可以直接内联在业务代码中判断非常适合「删除前确认后紧接着执行 API 调用」这类流程先await reveal()若未取消则用data继续后续步骤全程不需要把状态塞进组件 data。结果对象的类型契约reveal()的 Promise 结果类型由UseConfirmDialogRevealResultC, D定义它是一个可辨识联合discriminated unionisCanceled就是判别字段export type UseConfirmDialogRevealResultC, D | { data?: C isCanceled: false } | { data?: D isCanceled: true }确认时isCanceled: falsedata是confirm(data)传入的ConfirmData取消时isCanceled: truedata是cancel(data)传入的CancelData。在 TypeScript 中if (!isCanceled)分支内data会被自动收窄为ConfirmData这是该 composable 类型安全的核心所在。完整类型声明理解完整类型签名有助于在泛型场景下正确使用。以下是文档给出的完整声明export type UseConfirmDialogRevealResultC, D | { data?: C isCanceled: false } | { data?: D isCanceled: true } export interface UseConfirmDialogReturnRevealData, ConfirmData, CancelData { /** * Revealing state */ isRevealed: ComputedRefboolean /** * Opens the dialog. * Create promise and return it. Triggers onReveal hook. */ reveal: ( data?: RevealData, ) PromiseUseConfirmDialogRevealResultConfirmData, CancelData /** * Confirms and closes the dialog. Triggers a callback inside onConfirm hook. * Resolves promise from reveal() with data and isCanceled ref with false value. * Can accept any data and to pass it to onConfirm hook. */ confirm: (data?: ConfirmData) void /** * Cancels and closes the dialog. Triggers a callback inside onCancel hook. * Resolves promise from reveal() with data and isCanceled ref with true value. * Can accept any data and to pass it to onCancel hook. */ cancel: (data?: CancelData) void /** * Event Hook to be triggered right before dialog creating. */ onReveal: EventHookOnRevealData /** * Event Hook to be called on confirm(). * Gets data object from confirm function. */ onConfirm: EventHookOnConfirmData /** * Event Hook to be called on cancel(). * Gets data object from cancel function. */ onCancel: EventHookOnCancelData }几个值得注意的细节isRevealed是ComputedRefboolean在模板中直接使用会自动解包无需写.valueonConfirm/onCancel回调的入参来自confirm(data)/cancel(data)所以「把对象传给确认函数 → 钩子里拿到对象」是一条完整的数据链路reveal(data?)的RevealData会传给onReveal适合做弹窗打开前的数据预填三个钩子都是EventHookOnT其回调签名允许(param: T) void形式并返回{ off }以便按需解绑这与createEventHook的返回结构一致。在 airi 项目中的落地建议airi 是一个 Vue 3 全栈仓库vueuse/core是各应用与包共享的依赖例如 packages/stage-ui/package.json 中声明了vueuse/core: catalog:因此useConfirmDialog可以直接在所有 Vue 应用apps/stage-web、apps/stage-tamagotchi、apps/component-calling等中使用无需额外安装依赖。结合仓库现有实现以下几类场景与useConfirmDialog高度契合危险操作二次确认角色/会话的删除确认。相比仓库 characters 设置页 中使用的浏览器原生confirm()useConfirmDialog能提供自定义样式弹窗、携带上下文数据并保持类型安全会话切换确认切换前若有未保存内容需要用户确认可把待切换的目标会话 id 作为reveal(id)的载荷传入确认后在onConfirm中拿到 id 继续执行多级确认链dialog chains例如「确认重置 → 再次确认清空全部数据」的两级链路每一级各自持有独立的useConfirmDialog实例用 Promise 串起来即可与 store 状态共享传入外部shallowRef作为revealed参数使弹窗可见性与 Pinia 状态联动。弹窗 UI 层的参考实现useConfirmDialog只负责逻辑骨架弹窗视觉层可参考仓库中已有的两类实现风格受控组件风格基于reka-ui的DialogRoot/DialogPortal/DialogOverlay/DialogContent配合v-model:open受控参见 sessions-dialog.vue条件渲染风格用Transition包裹v-if条件块并定义modal-fade进出场动画参见 JournalPreviewModal.vue 中的modal-fade-enter-active等过渡类。将useConfirmDialog的isRevealed接入上述任一弹窗的显示条件再把confirm/cancel绑定到弹窗的动作按钮上即可快速获得一套完整、可维护的确认交互。小结useConfirmDialog用不到 50 行源码级别的 API 面同时提供了「事件钩子」与「Promise」两种确认交互范式钩子模式适合 UI 与副作用解耦、多个观察者需要监听确认结果的场景Promise 模式适合命令式流程编排await reveal()后按isCanceled分支处理数据全程类型安全底层由createEventHook提供事件能力与 VueUse 生态其他 Utilities 一脉相承在 airi 这类已内置vueuse/core的 Vue 3 项目中它是替代原生confirm()、统一确认弹窗体验的首选 composable。无论你是要给角色删除加一道确认还是构建复杂的多级授权链路都可以从useConfirmDialog开始——它把「弹窗状态、用户抉择、业务副作用」三者之间最啰嗦的胶水代码一次性收敛干净。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表