Airi 开发实践:深入 VueUse `createEventHook` 事件钩子工厂的使用与类型设计

发布时间:2026/9/10 10:37:18

Airi 开发实践:深入 VueUse `createEventHook` 事件钩子工厂的使用与类型设计 Airi 开发实践深入 VueUsecreateEventHook事件钩子工厂的使用与类型设计【免费下载链接】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/airicreateEventHook是 VueUse 提供的一个轻量级事件钩子event hook工厂函数位于 Utilities工具类分类中用于在不引入事件总线、不依赖组件树的前提下为任意函数/Composable 添加可订阅、可触发、可清理的监听能力。本篇基于 airi 仓库中 VueUse 技能参考文档 createEventHook.md结合其 Type Declarations 与仓库内的配套参考与依赖情况完整讲解该 API 的生产方/消费方两种用法、四类返回方法on/off/trigger/clear的语义、回调类型推导规则以及它在 VueUse 内部如useWebNotification、useConfirmDialog与 Vue 3 单文件组件中的落地方式。读完本篇你将掌握如何为自己的useXxx组合式函数设计一套类型安全、可异步触发、便于解除订阅的钩子接口。createEventHook是什么按照参考文档中的定义createEventHook是Utility for creating event hooks用于创建事件钩子的工具函数。它的典型价值在于把一个未来的事件通知机制封装成独立实例然后将其中的监听 API 暴露给外部调用方。从 SKILL.md 的分类表可以看到createEventHook与useEventBus、useConfirmDialog、useMemoize等同属于 Utilities 工具分类默认调用规则为AUTO——即开发 Vue 3 / Nuxt 3 功能时若需求匹配即可优先选用。与之配合的同类能力函数定位createEventHook为单个自定义 Composable 创建独立的事件钩子实例useConfirmDialogCreates event hooks to support modals and confirmation dialog chains基于事件钩子封装模态框确认链useEventBus全局/具名事件总线属于跨模块广播方案相较事件总线createEventHook的粒度更小、更贴近函数内部状态——它的事件源与事件消费被封装在同一 Composable 的返回接口里不需要全局命名、不需要担心总线事件串扰天然适合作为组件库与业务 Composable 的内部通信原语。核心用法生产方视角参考文档给出的第一个场景是编写一个使用createEventHook的函数生产方/提供方。原文示例以封装fetch为例import { createEventHook } from vueuse/core export function useMyFetch(url) { const fetchResult createEventHookResponse() const fetchError createEventHookany() fetch(url) .then(result fetchResult.trigger(result)) .catch(error fetchError.trigger(error.message)) return { onResult: fetchResult.on, onError: fetchError.on, } }这个例子展示了几个关键设计点每个事件通道独立实例化。这里创建了fetchResult与fetchError两个钩子实例分别承载成功与失败两条通道而不是让监听方自行判断回调参数职责清晰。泛型即载荷类型。createEventHookResponse()表示该钩子触发的载荷是一个Response消费方回调的参数会获得对应的类型提示。只暴露on。生产方把fetchResult.on/fetchError.on放入返回值监听方只能订阅无法在外部随意trigger伪造事件权限边界天然可控。补充一个类型更严谨的封装原示例为了简洁省略了泛型参数的类型标注实际开发中可以为url与触发通道补充完整类型让整个函数对外更可用import { createEventHook } from vueuse/core export interface FetchPayload { ok: boolean status: number data?: unknown } export function useMyFetch(url: string) { const fetchResult createEventHookFetchPayload() const fetchError createEventHook{ message: string; status?: number }() const run async () { try { const res await fetch(url) fetchResult.trigger({ ok: res.ok, status: res.status, data: res.ok ? await res.json() : undefined, }) } catch (error) { fetchError.trigger({ message: (error as Error).message }) } } return { onResult: fetchResult.on, onError: fetchError.on, run, } }这里run让请求可以按需发起而onResult/onError保持与参考文档一致的订阅口。核心用法消费方视角参考文档的第二个场景是在Vue 单文件组件script setup中消费上述函数script setup langts import { useMyFetch } from ./my-fetch-function const { onResult, onError } useMyFetch(my api url) onResult((result) { console.log(result) }) onError((error) { console.error(error) }) /script消费方的体验与 Vue 3 中emits/事件监听的心智模型保持一致onResult(callback)注册成功回调result参数类型即为生产方声明时的Response/FetchPayloadonError(callback)注册失败回调由于生产方只暴露on组件内拿不到trigger无法误触发。不要忽略订阅的返回值on的返回值是一个携带off方法的对象见下方类型声明这意味着订阅是可显式解除的。在需要精确控制生命周期的场景应保存并调用它import { onBeforeUnmount } from vue import { useMyFetch } from ./my-fetch-function const { onResult, onError } useMyFetch(/api/profile) const subscription onResult((result) { console.log(profile updated, result) }) // 组件卸载前解除订阅避免回调泄漏 onBeforeUnmount(() { subscription.off() })需要说明的是与onMounted/onScopeDispose这类自动随作用域清理的 VueUse API 不同createEventHook本身不绑定 Vue 生命周期是否清理、何时清理由使用方自行管理。在组件中按上述方式手动off或调用钩子的clear()是推荐实践。类型声明逐条解读参考文档完整给出了createEventHook的类型声明createEventHook.md逐条拆解如下type CallbackT IsAnyT extends true ? (...param: any) void : [T] extends [void] ? (...param: unknown[]) void : [T] extends [any[]] ? (...param: T) void : (...param: [T, ...unknown[]]) voidCallbackT是一个按载荷类型 T 分派回调签名的条件类型若T是any通过 VueUse 内部类型工具IsAny判断回调接受任意参数(...param: any) void用于保留类型灵活性若T为void表示无载荷事件回调可以接收不定数量的未知参数(...param: unknown[]) void若T本身是元组any[]的元组形态如[string, number]则回调直接展开接收(...param: T) void支持多参数事件其余一般情况回调接收[T, ...unknown[]]——即首个参数为载荷T后续保留未知的扩展参数。export type EventHookOnT any (fn: CallbackT) { off: () void } export type EventHookOffT any (fn: CallbackT) void export type EventHookTriggerT any ( ...param: ParametersCallbackT ) Promiseunknown[] export interface EventHookT any { on: EventHookOnT off: EventHookOffT trigger: EventHookTriggerT clear: () void } export type EventHookReturnT EventHookT四个方法与一个EventHookT接口共同构成了完整钩子能力成员签名要点语义on(fn) { off }注册监听回调返回带off的句柄以便单独解除off(fn) void从监听集合中移除指定回调trigger(...param) Promiseunknown[]触发事件异步执行全部监听并聚合返回每个回调的结果数组clear() void清空所有监听回调值得强调的语义是trigger它是一个异步触发入口返回Promiseunknown[]。这意味着一方面监听回调可以同步执行后由Promise.all风格聚合另一方面调用方可await触发过程。这也解释了为什么trigger的入参类型取ParametersCallbackT——它必须与CallbackT推导出的回调参数签名严格一致。内部实现可以如何还原从类型声明可以推断最朴素的内部实现与Set 存回调 trigger 聚合执行的结构一致。参考文档的注释也说明该实现的灵感源自 vue-apollo 的useEventHook工具。据此还原一个语义等价的最小实现仅用于帮助理解非仓库源码export function createEventHookT any() { const fns: SetCallbackT new Set() const off (fn: CallbackT) { fns.delete(fn) } const on (fn: CallbackT) { fns.add(fn) return { off: () off(fn) } } const trigger (...param: ParametersCallbackT) Promise.all([...fns].map(fn fn(...param))) const clear () { fns.clear() } return { on, off, trigger, clear } }可以看到四个方法对应的底层操作分别是 Set 的add/delete/clear与批量调用整个工具没有任何 DOM、副作用依赖因此在非浏览器环境SSR、测试、Node中也能安全使用——参考文档头部还标注了__NO_SIDE_EFFECTS__便于打包器做 tree-shaking。VueUse 自身如何使用它两个可对照的仓库内参考在 airi 仓库的同一套技能参考集中还能找到createEventHook的两个被使用者是理解该工具定位的最佳佐证。useWebNotification钩子作为返回接口的载体参考 useWebNotification.mdVueUse 的useWebNotificationWeb 通知 API 的响应式封装正是利用createEventHook为外部暴露事件订阅import { useWebNotification } from vueuse/core const { onClick, onShow, onError, onClose } useWebNotification() onClick((evt: Event) { // 处理通知被点击 }) onShow((evt: Event) { // 处理通知展示 }) onError((evt: Event) { // 处理通知出错 }) onClose((evt: Event) { // 处理通知关闭 })在该文档的类型声明中返回值明确写着onClick: EventHookOnEvent、onShow: EventHookOnEvent等——也就是文档所注释的 This composable also utilizes the createEventHook utility。这是 VueUse 官方代码库中用createEventHook为 Composable 增加事件回调的典型范式。useConfirmDialog多个钩子组合成流程状态机同样在 Utilities 分类中useConfirmDialog 被描述为 Creates event hooks to support modals and confirmation dialog chains。它的整体设计思路是把确认弹窗抽象为reveal打开并等待、confirm、cancel等事件通道内部由多个createEventHook实例支撑监听方通过onConfirm/onCancel订阅用户操作结果。可以把它看作用一组事件钩子编排交互状态的进阶示例——当单个通道不够用时按事件语义拆分成多个钩子实例即可组合出更复杂的流程。在 airi 仓库中的落地前提createEventHook属于 VueUse 对外导出的 API参考 createEventHook.md 中import { createEventHook } from vueuse/core的用法。airi 仓库本身通过 pnpm workspace catalog 依赖管理统一引入 VueUse例如 apps/stage-pocket/package.json 中声明了vueuse/core: catalog:在 packages 层packages/stage-ui/package.json、packages/stage-shared/package.json、packages/electron-vueuse/package.json 等均依赖vueuse/core覆盖 Electron 渲染层、Web 应用与共享 UI 包等不同运行环境。因此在 airi 的各 Vue 3 应用与 UI 包中遵循这套技能规范开发自定义 Composable 时createEventHook均可直接按AUTO规则选用当需要一个仅面向当前函数返回接口、可订阅/可触发/可清理的事件通道时优先考虑它而不是自己维护回调数组或引入全局事件总线。实战建议与常见陷阱优先暴露 on、藏起 trigger。参考文档的useMyFetch只返回onResult/onError这是刻意设计的封装边界。只有当调用方确实需要从外部触发时才返回trigger。每个事件通道一个实例。把成功/失败打开/确认/取消等不同语义拆成独立钩子比单一钩子内传联合类型更易读也与useConfirmDialog的多钩子组合风格一致。trigger是异步的。它返回Promiseunknown[]内部等价于对全部监听做批量调用并聚合结果。若某个监听回调抛错需要留意 Promise 的拒绝传播必要时在回调内自行 try/catch。类型先行的载荷设计。利用createEventHookT的泛型载荷为void表示纯信号事件载荷为元组如[id: string, count: number]时回调可展开为多参数载荷为普通对象类型时回调首参即该对象。手动管理清理时机。on返回的{ off }与钩子的clear()是唯一清理途径不会随组件卸载自动触发在script setup里请配合onBeforeUnmount解除订阅避免对已卸载组件产生引用与多余回调。按需聚合多个通道。若觉得回调注册心智负担偏重可先确认需求是否真的需要事件式通知——否则更简单的做法是返回ref/computed状态让消费方 watch这也是本套技能文档中 State 分类各函数如useAsyncState的适用场景。小结createEventHook用极小的 API 面积on/off/trigger/clear为 Vue 3 Composable 补齐了事件通知这一能力维度生产方可以安全地只暴露订阅接口消费方可以获得由泛型推导的强类型回调触发方可以异步批量通知并拿到每个监听的结果。无论你是像useMyFetch那样为异步请求封装结果/错误通知还是像 VueUse 官方在useWebNotification、useConfirmDialog中那样为高阶 Composable 暴露事件式接口都可以直接复用本文梳理的模式与类型约定。仓库内可继续查阅的资料参考文档本体.agents/skills/vueuse-functions/references/createEventHook.mdVueUse 函数分类与调用规则.agents/skills/vueuse-functions/SKILL.mduseWebNotification对EventHookOn的使用.agents/skills/vueuse-functions/references/useWebNotification.mduseConfirmDialog的功能定位.agents/skills/vueuse-functions/SKILL.mdairi 中依赖 VueUse 的示例apps/stage-pocket/package.json、packages/stage-ui/package.json【免费下载链接】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),仅供参考
延伸阅读

更多相关文章

2026/9/10 10:37:18

没有AB实验怎么做因果推断?PSM倾向得分匹配实战指南

做AB实验做得久了,你会慢慢意识到一个扎心的事实:不是所有场景都能给你一个干净的随机分组。用户在产品里已经走了一圈,你没法把他们“打回原形”重新抽签。这时候还硬套AB实验的框架,出来的结论不是偏的就是假的,甚至…

2026/9/10 11:37:29

C++数据库连接池设计与性能优化实践

1. 为什么需要数据库连接池? 在C后端开发中,数据库操作是系统性能的关键瓶颈之一。每次建立数据库连接都需要完成TCP三次握手、认证、权限检查等系列操作,实测MySQL创建单个连接的平均耗时在50-200ms之间。在高并发场景下,频繁创建…

2026/9/10 11:37:29

Android打包流程详解:从编译到签名优化

1. Android打包流程概述作为一名Android开发者,每天都要经历数十次打包过程。但你真的了解这个看似简单的操作背后发生了什么吗?从点击"Build"按钮到生成最终的APK/AAB文件,Android Studio完成了一系列复杂的编译、转换和优化工作。…

2026/9/10 11:37:29

mpv 配置指南:4 步诊断法搞定卡顿、字幕与低功耗播放

mpv 配置指南:4 步诊断法搞定卡顿、字幕与低功耗播放 【免费下载链接】mpv 🎥 Command line media player 项目地址: https://gitcode.com/GitHub_Trending/mp/mpv mpv 配置讲究"先诊断后开方":多数翻车不是因为参数抄得不对…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码