Airi 仓库实践:VueUse watchTriggerable 手动触发式响应监听深入解析
Airi 仓库实践VueUse watchTriggerable 手动触发式响应监听深入解析【免费下载链接】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在 Vue 3 响应式体系中watch是“值变了才回调”的被动监听而不少业务场景手动触发副作用、带onCleanup的异步任务需要一种“我可以随时主动执行一次回调”的监听器。VueUse 的watchTriggerable正是为此设计的watch增强包装它在标准监听之上额外返回一个trigger函数可立即执行WatchCallback。本文基于 airi 仓库中收录的 VueUse 技能参考文档.agents/skills/vueuse-functions/references/watchTriggerable.md展开完整覆盖其用法示例、onCleanup场景与类型声明并结合 airi monorepo 中实际锁定的 VueUse 版本与技能目录结构给出工程落地依据。1. watchTriggerable 解决的问题与在 airi 中的定位watchTriggerable的官方定义一句话概括“Watch that can be triggered manually”——一个支持手动触发回调的watch包装器返回值在标准watch能力基础上额外提供trigger用于立即执行一次WatchCallback而不必等待 source 真正发生变化。在 airi 仓库中该函数的上下文是.agents/skills/vueuse-functions/目录下从 VueUse 官方技能库同步sync而来的“决策与实现指南”。其入口 SKILL.md 明确要求在 Vue.js / Nuxt 项目协助开发时优先用 VueUse 组合式函数替代手写代码以提升可读性、可维护性与性能watchTriggerable被归类在 Watch 分类下Invocation 规则为AUTO适用时自动优先选用FunctionDescriptionInvocationwatchTriggerableWatch that can be triggered manuallyAUTO该技能目录的同步来源记录见 SYNC.md源为vendor/vueuse/skills/vueuse-functionsGit SHA 为b6bb79b99fb1f1dba1f907829676a651735bbc10同步日期 2026-06-22。也就是说本文展开的参考文档内容与该版本的 VueUse 官方文档保持一致。适用前提需要 Vue 3 及以上或 Nuxt 3 及以上项目并已安装vueuse/core。airi 仓库满足这一前提——根 pnpm-workspace.yaml 的 catalog 中锁定vueuse/core: ^14.4.0且 packages/stage-ui/package.json 与 packages/ui/package.json 均以catalog:方式依赖它是仓库内 UI 层的主要消费方。2. 基本用法source、回调与 trigger参考文档给出的标准用法如下完整继承自 watchTriggerable.mdimport { watchTriggerable } from vueuse/core import { nextTick, shallowRef } from vue const source shallowRef(0) const { trigger, ignoreUpdates } watchTriggerable( source, v console.log(Changed to ${v}!), ) source.value bar await nextTick() // logs: Changed to bar! // Execution of WatchCallback via trigger does not require waiting trigger() // logs: Changed to bar!这段示例揭示了三个关键行为返回值是“可解构的对象”而非WatchHandle。与原生watch返回一个stop函数不同watchTriggerable返回WatchTriggerableReturn其中至少包含trigger手动触发回调从类型继承关系看见第 4 节该返回类型extends WatchIgnorableReturn因此ignoreUpdates、ignorePrevAsyncUpdates、stop同样可用——示例中解构出的ignoreUpdates即来自此继承。被动路径与普通watch完全一致source.value bar之后回调在await nextTick()后执行。文档示例未传任何 options因此沿用watch的默认行为即flush: pre这一点可参考同族 watchIgnorable.md 中“WatchOptionFlush timing”一节的说明默认按flush: pre工作需要时可通过 options 显式指定flush与immediate。trigger()是同步执行无需等待 nextTick源码注释明确写着 “Execution of WatchCallback viatriggerdoes not require waiting”。这正是它区别于“手动把source.value改回去再等一次 flush”的自造轮子的地方——直接触发走的是回调执行路径本身而不是依赖响应式批处理调度。一个典型语义把“值变化时执行”和“我想现在执行一次”合并到同一个回调里。例如 airi 的 Web 端 UIstage-web、stage-ui等 Vue 应用中像“模型驱动状态变化时刷新预览”这类需求既可以由 ref 变化自动驱动也可以由用户点击某个按钮手动执行同一套刷新逻辑watchTriggerable让这两条路径共享一个回调实现避免复制粘贴。3. 重点场景手动执行带 onCleanup 的 watch文档专门用onCleanup小节强调了一个难以自造的能力When you want to manually call awatchthat uses the onCleanup parameter; simply taking theWatchCallbackout and calling it doesnt make it easy to implement theonCleanupparameter. UsingwatchTriggerablewill solve this problem.原因是onCleanup是 Vue 在 watch 回调执行生命周期内部注入的参数每次触发前会执行上一次注册的清理函数。如果你把回调函数单独拿出来直接调用就拿不到这个参数清理逻辑无法纳入统一生命周期。watchTriggerable的trigger走的是与自动触发相同的回调通道因此onCleanup在手动触发时同样生效。文档示例完整保留import { watchTriggerable } from vueuse/core import { shallowRef } from vue const source shallowRef(0) const { trigger } watchTriggerable( source, async (v, _, onCleanup) { let canceled false onCleanup(() canceled true) await new Promise(resolve setTimeout(resolve, 500)) if (canceled) return console.log(The value is ${v}\n) }, ) source.value 1 // no log await trigger() // logs (after 500 ms): The value is 1注意示例中source.value 1 // no log的注释回调内先注册了onCleanup并await500ms源值变化本身并不会让回调走完回调尚未开始异步执行日志被推迟/取消机制接管而await trigger()则会在 500ms 后打印The value is 1。这个模式适合“每次触发先取消上一次未完成的异步任务”的场景onCleanup(() canceled true)保证上一次回调若仍在await中会在本轮启动时被打上取消标记回调在异步完成后再检查canceled被取消的任务直接静默返回由于trigger返回FnReturnT见类型声明回调可以是async函数调用方可以await trigger()拿到结果——这在文档示例与返回类型trigger: () FnReturnT中均已体现。对于 airi 这类“实时语音、模型驱动、多渲染管线Live2D / MMD / Spine / Three”的 Vue 应用这类“触发即刷新、旧任务即取消”的模式在驱动状态切换、场景重载等路径上都有对应形态watchTriggerable提供了一个不依赖框架扩展的通用写法。4. 类型声明逐段解读文档给出的是完整的 TypeScript 声明原样继承自参考文档export interface WatchTriggerableReturn FnReturnT void, extends WatchIgnorableReturn { /** Execute WatchCallback immediately */ trigger: () FnReturnT } type OnCleanup (cleanupFn: () void) void export type WatchTriggerableCallbackV any, OV any, R void ( value: V, oldValue: OV, onCleanup: OnCleanup, ) R export declare function watchTriggerableT, FnReturnT( source: WatchSourceT, cb: WatchTriggerableCallbackT, T | undefined, FnReturnT, options?: WatchWithFilterOptionsboolean, ): WatchTriggerableReturnFnReturnT export declare function watchTriggerable T extends ReadonlyMultiWatchSources, FnReturnT, ( sources: [...T], cb: WatchTriggerableCallback MapSourcesT, MapOldSourcesT, true, FnReturnT , options?: WatchWithFilterOptionsboolean, ): WatchTriggerableReturnFnReturnT export declare function watchTriggerableT extends object, FnReturnT( source: T, cb: WatchTriggerableCallbackT, T | undefined, FnReturnT, options?: WatchWithFilterOptionsboolean, ): WatchTriggerableReturnFnReturnT可以逐段读出如下信息WatchTriggerableReturnFnReturnT核心成员只有一个trigger: () FnReturnT注释明确为 “ExecuteWatchCallbackimmediately”。它extends WatchIgnorableReturn——即自动继承了stop、ignoreUpdates、ignorePrevAsyncUpdates这意味着watchTriggerable天然具备“可忽略监听”的全部能力不需要再包一层watchIgnorable。WatchTriggerableCallbackV, OV, R回调签名是(value, oldValue, onCleanup) R。对比原生WatchCallback多了第三个参数onCleanup: OnCleanup且onCleanup的类型就是标准的(cleanupFn: () void) void——这就是第 3 节手动触发也能注册清理函数的类型层依据。第三个泛型R即回调返回值最终透传给trigger的返回类型FnReturnT因此await trigger()能拿到 async 回调的结果。三个重载对应三种 source 形态单值源source: WatchSourceTref、computed、getter 均可cb收到(value: T, oldValue: T | undefined, onCleanup)多源数组sources: [...T]T extends ReadonlyMultiWatchSourcescb收到MapSourcesT各源当前值的元组/数组映射与MapOldSourcesT, true旧值映射带true表示允许 undefined响应式对象源source: T extends object对整个 reactive 对象做深度语义的监听具体深度行为仍受options.deep控制。options 是WatchWithFilterOptionsboolean它extends WatchOptionsboolean ConfigurableEventFilter参见同族 watchWithFilter.md 的类型声明因此除了标准watch选项immediate、deep、flush等还可以传eventFilter例如debounceFilter、throttledFilter、pausableFilter。也就是说watchTriggerable可以叠加事件过滤器实现“带防抖/节流/可暂停语义的可手动触发监听”。5. 与同族 Watch 函数的关系airi 的 VueUse 技能参考目录下收录了完整的 Watch 分类函数表见 SKILL.md 的 Watch 小节watchTriggerable与它们的关系可以这样理解watchWithFilter提供eventFilter控制防抖、节流、可暂停过滤器返回普通WatchHandlewatchIgnorable返回ignoreUpdates/ignorePrevAsyncUpdates/stop用于忽略特定来源更新watchTriggerable选项类型直接复用WatchWithFilterOptionsboolean、返回类型直接继承WatchIgnorableReturn——从源码结构看它是把“可过滤”与“可忽略”两种能力都并入后再叠加一个trigger的手动触发入口。选型上只需要忽略更新用watchIgnorable只需要防抖/节流用watchDebounced/watchThrottled需要“手动主动执行一次且回调内依赖onCleanup生命周期”时选watchTriggerable。6. 小结在 airi 工程中如何落地结合仓库证据使用watchTriggerable的完整前提与步骤确认环境项目基于 Vue 3 / Nuxt 3依赖vueuse/core。airi 中该依赖已在根 catalog 锁定为^14.4.0pnpm-workspace.yamlpackages/stage-ui、packages/ui等包已经依赖它直接import { watchTriggerable } from vueuse/core即可。确认场景需要“source 变化时自动回调 外部可随时手动执行同一回调”尤其是回调内使用了onCleanup注册清理、或回调是async且需要await trigger()获取结果时。确认选项不传 options 时沿用watch默认flush: pre如需防抖/节流/暂停通过eventFilter传入类型由WatchWithFilterOptions提供需要忽略程序化更新时直接解构返回对象里的ignoreUpdates无需额外包装。参考文档定位本仓库的权威参考位于 .agents/skills/vueuse-functions/references/watchTriggerable.md同目录下的watchWithFilter.md、watchIgnorable.md提供了相关类型WatchWithFilterOptions、WatchIgnorableReturn的完整声明可交叉查阅。【免费下载链接】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),仅供参考