
airi 项目实战VueUse whenever —— 只在“变真时”执行的 watch 简写【免费下载链接】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/airiwhenever是 VueUsevueuse/core中 Watch 分类下的一个核心 composable专门用来“监听某个响应式值变为真值truthy的那一刻”执行回调。airi 仓库在性能可视化工具的快捷键处理和跨应用登录标志位needsLogin的触发逻辑中都直接使用了它。读完本篇你能掌握whenever的完整语义回调参数、getter 源、选项与类型声明并学会在 airi 这类 Vue 3 Pinia 多端应用中用一行代码替代“watch if 判真”的样板代码。一、核心定位watch 真值过滤的等价简写whenever的官方定义是一句话Shorthand for watching value to be truthy监听值变为真值的简写。它等价于在watch回调中手动加if判断import { whenever } from vueuse/core // 这个写法 whenever(ready, () console.log(state)) // 等价于 watch(ready, (isReady) { if (isReady) console.log(state) })这个等价关系揭示了它的完整语义从源码结构看即“带真值过滤的 watch”只响应“变真”的跃迁由于watch本身只在值变化时触发whenever实际上只在源值从假值false、0、、null、undefined变为真值时执行回调。值在真值之间互相变化例如1变2不会触发。源值初始即为真时不触发whenever不默认immediate如果注册监听时源值已经是真值回调不会立即执行需要立即检查初始状态时可通过选项传入{ immediate: true }选项与watch完全一致见下文第四节。返回值是WatchHandle与watch一样可用于手动停止监听并在组件卸载时随 effect scope 自动清理不产生泄漏。在 airi 仓库的 AI 开发规范 SKILL.md 中whenever被列在 Watch 分类下且 Invocation 标记为AUTO——即“凡是用它能实现的需求优先使用它而不是手写 watch if”这正是它存在的工程价值用一个语义化的名字表达“条件满足时执行一次副作用”。二、完整用法从文档示例到四种调用形态以下内容完整继承自 airi 仓库内置的 VueUse 参考文档 whenever.md。基本用法与 useAsyncState 配合典型场景是“异步数据就绪后才执行副作用”import { useAsyncState, whenever } from vueuse/core const { state, isReady } useAsyncState( fetch(https://jsonplaceholder.typicode.com/todos/1).then(t t.json()), {}, ) whenever(isReady, () console.log(state))isReady从false变为true的那一刻state已可用回调恰好在此时执行——无需手写if (isReady)。回调参数与 watch 完全一致与watch相同回调以cb(value, oldValue, onInvalidate)的签名被调用。在真值场景下最有用的通常是前两个参数value是新的真值oldValue是变化前的旧值可以据此计算增量import { whenever } from vueuse/core whenever(height, (current, lastHeight) { if (current lastHeight) console.log(Increasing height by ${current - lastHeight}) })源可以是 getter 函数与watch一致第一个参数可以传一个 getter每次变化时重新求值从而监听“派生条件”而非某个具体 refimport { whenever } from vueuse/core // 监听“counter 恰好等于 7”这个条件成立 whenever( () counter.value 7, () console.log(counter is 7 now!), )选项与 watch 完全一致第三个参数透传给watch的WatchOptions例如 flush 时机whenever( () counter.value 7, () console.log(counter is 7 now!), { flush: sync }, )flush可选pre默认组件更新前、post组件更新后、sync同步立即执行{ immediate: true }、{ deep: true }等选项同样适用。三、类型声明Truthy 与 once 选项官方类型声明完整如下引自 whenever.mdtype TruthyT T extends false | null | undefined ? never : T export interface WheneverOptions Immediate boolean, extends WatchOptionsImmediate { /** * Only trigger once when the condition is met * * Override the once option in WatchOptions * * default false */ once?: boolean } /** * Shorthand for watching value to be truthy * * see https://vueuse.org/whenever */ export declare function wheneverT( source: WatchSourceT, cb: WatchCallbackTruthyT, T | undefined, options?: WheneverOptionstrue, ): WatchHandle export declare function wheneverT( source: WatchSourceT, cb: WatchCallbackTruthyT, T, options?: WheneverOptionsfalse, ): WatchHandle三个值得注意的点TruthyT收窄了回调参数的值类型。例如源是Refboolean普通watch的回调拿到value: boolean而whenever的回调拿到的是TruthyT——在触发点保证为真值类型false | null | undefined被排除为never省去回调内的二次判空。Once选项WheneverOptions.once默认false表示条件满足、回调触发一次后自动停止监听语义上覆盖了WatchOptions中的同名once选项。两个重载区分 oldValue 是否可能为undefinedWatchCallbackTruthyT, T | undefined与WatchCallbackTruthyT, T对应 immediate 语义下 oldValue 的有无由编译器根据你传入的选项类型自动选中。四、airi 仓库中的两处真实用法airi 是一个 pnpm workspace 单体仓库各应用Web 舞台、Pocket、Electron 桌面等与共享包packages/stage-ui统一通过 catalog 版本vueuse/core: ^14.4.0见 pnpm-workspace.yaml引用 VueUse。仓库中至少有两处whenever的生产级用法分别对应它最典型的两种场景。场景一快捷键“按下即触发” —— 性能可视化页文件performance-visualizer.vueconst magicKeys useMagicKeys() whenever(magicKeys[ctrlaltl], () lagStore.toggleAll(true)) whenever(magicKeys[ctrlaltk], () lagStore.toggleAll(false))useMagicKeys()返回一个响应式的按键映射magicKeys[ctrlaltl]在组合键按下时变为true、松开时变回false。如果直接用watch每次回调都要写if (pressed) { ... }来忽略“松开”这一次变化换成whenever后回调天然只在“按下”false → true跃迁时执行一次语义与意图完全对齐。这个页面是桌面端设置里用于可视化性能指标帧率、内存等的开发者工具快捷键用于一键开/关全部指标的采集。从源码结构看这正是whenever相对watch的核心收益把“按键释放导致值变假”这一次无意义的触发在类型与逻辑层面都过滤掉了。场景二跨应用登录标志位 —— needsLogin 事件旗标文件auth.ts// Cross-app user must log in flag. Setting this to true triggers an // immediate OIDC redirect on web (mobile desktop). Electron skips this // path because controls-island-auth-button listens for IPC and handles // sign-in in the main process. const needsLogin ref(false) const { isMobile } useBreakpoints() whenever(needsLogin, async () { if (isStageTamagotchi()) return // Consume the request before opening an external browser. Pocket stays // mounted when the user cancels there, so leaving this true would make // the next button click a no-op instead of starting a new OIDC flow. needsLogin.value false await triggerSignIn() }) // Reset the flag if the viewport class flips, so a stale needsLogin from a // previous breakpoint does not surface again on resize. watch(isMobile, () needsLogin.value false)这里needsLogin是一个“事件旗标”event flag欢迎页、优惠券弹窗、登录失败的auth-fetch等多处 UI 只需要把authStore.needsLogin true例如 step-welcome.vue 中的一行赋值全局唯一的whenever监听器就会在 false → true 跃迁时拉起 OIDC 登录流程。两个工程细节值得借鉴消费式复位回调内先needsLogin.value false再await triggerSignIn()。注释解释了原因——Pocket 页签在外部浏览器取消登录时仍然保持挂载若标志位一直是true下一次点击无法产生新的跃迁登录按钮就成了 no-op。测试兜底auth.test.ts 中的用例 “allows sign-in to be requested again after an external flow is canceled” 显式验证了这一模式置true→ 断言triggerSignIn被调用且标志位复位为false→ 再次置true→ 断言第二次登录流程能正常触发。测试注释还点出了whenever依赖“false → true 跃迁”这一语义的根因ROOT CAUSE说明是理解第三节“只响应变真跃迁”的绝佳实证。五、选型whenever vs watchOnce vs 普通 watch需求推荐写法值变为真值时执行可反复触发whenever(source, cb)条件满足后只执行一次即停止whenever(source, cb, { once: true })值任意变化都执行watch(source, cb)值变为真值且只需一次、语义更直白watchOnce(source, () { if (v) ... })或whenever(..., { once: true })同属 Watch 分类的watchOncewatchOnce.md是{ once: true }的简写但它不区分新旧值真假而whenever的{ once: true }语义是“条件真值满足一次即停”二者解决的问题不同。另外若你需要“等到值满足某个条件再继续后续流程”的 Promise 化写法应选用until同为 Watch 分类见 until.md它与whenever形成“命令式回调 / 异步等待”的互补。六、使用要点小结whenever适用于“标志位 / 就绪状态 / 按键状态”这类布尔语义的响应式值把“监听 判真 执行”压缩为一行回调签名与watch完全一致value, oldValue, onInvalidate且value被TruthyT收窄触发点即为真值选项直接透传WatchOptionsflush、immediate、deep等并额外提供once控制一次性触发它依赖“false → true 跃迁”注册时源值已为真则不触发若下游流程可能让标志位残留为true务必像 auth.ts 那样在回调内消费复位标志位并用 auth.test.ts 这类用例固化“可重复触发”的行为airi 的 AI 开发规范 SKILL.md 将其标记为AUTO级函数在 Vue 3 项目中遇到“条件为真时做事”的诉求应优先想到whenever而非手写watch。【免费下载链接】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),仅供参考