ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VueUse watchOnce 详解:只触发一次的单次监听器,从用法到源码实现

VueUse watchOnce 详解:只触发一次的单次监听器,从用法到源码实现 前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载VueUse 的watchOnce是 Vue 3watch的一个简洁封装shorthand等价于开启{ once: true }选项的监听器当回调触发一次之后监听器会被自动停止。本文将以 VueUse 仓库中的官方参考文档与真实源码、测试为依据完整讲解watchOnce的用法、类型签名、底层实现原理并给出可复用的实战场景帮助你在只需响应一次变化的场景中写出更干净的代码。为什么需要 watchOnce在 Vue 3 开发中watch是最常用的响应式能力之一。但在不少业务场景里我们并不希望监听器持续响应等待某个异步状态第一次变为就绪后只做一次初始化等待某个全局状态如登录状态、用户信息第一次到位后执行一次性逻辑等待某个元素尺寸、路由参数首次变化后触发一次副作用。如果每次都手动声明{ once: true }并处理停止逻辑代码会变得啰嗦。Vue 3 的watch原生支持once选项在回调触发一次后自动停止监听watchOnce正是对这一能力的极简封装——正如官方参考文档 skills/vueuse-functions/references/watchOnce.md 所描述Shorthand for watching value with{ once: true }. Once the callback fires once, the watcher will be stopped.即监听值并开启once的简写回调触发一次后监听器即停止。基本用法watchOnce的 API 形态与 Vue 的watch几乎完全一致只是无需再手动传入once选项。参考 packages/shared/watchOnce/index.md 中的官方示例import { watchOnce } from vueuse/core watchOnce(source, () { // 只会触发一次 console.log(source changed!) })当source变化导致回调执行一次之后监听器随即被停止后续source的再次变化不会再触发回调。它同样支持传入一个 getter 函数、ref/shallowRef、reactive对象以及多个数据源组成的数组作为第一个参数用法与watch完全对齐。与原生 watch 的关系从语义上讲watchOnce(source, cb, options)完全等价于import { watch } from vue watch(source, cb, { ...options, once: true, })区别仅在于watchOnce把once: true作为固定语义内建调用方不能再传once选项去覆盖它其余一切watch能力flush、deep、immediate、onTrack、onTrigger等选项均可通过第三个参数透传。因此watchOnce并不是一个独立的新机制而是基于 Vue 3 原生oncewatcher 的类型化简写。官方文档提到可参考 Vue 官方关于 once watchers 的说明对应到本仓库内也可对照同一系列的watchImmediate{ immediate: true }的封装见 packages/shared/watchImmediate/index.md来理解这类单选项封装的家族式设计。完整类型签名参考文档 skills/vueuse-functions/references/watchOnce.md 中给出了完整的 TypeScript 声明共包含三个重载覆盖了watch支持的三种数据源形态export declare function watchOnceT( source: WatchSourceT, cb: WatchCallbackT, T | undefined, options?: OmitWatchOptionstrue, once, ): WatchHandle export declare function watchOnceT extends ReadonlyMultiWatchSources( source: [...T], cb: WatchCallbackMapSourcesT, MapOldSourcesT, true, options?: OmitWatchOptionstrue, once, ): WatchHandle export declare function watchOnceT extends object( source: T, cb: WatchCallbackT, T | undefined, options?: OmitWatchOptionstrue, once, ): WatchHandle逐条解读这三个重载单个数据源source: WatchSourceT覆盖ref、getter 函数等单一监听源回调收到新值T与旧值T | undefined首次触发时旧值为undefined。多数据源数组source: [...T]其中T extends ReadonlyMultiWatchSources回调参数由MapSourcesT与MapOldSourcesT, true推导即多个源的新值数组与多个源的旧值数组与原生watch([a, b], ...)的类型行为一致。响应式对象source: T extends object直接监听整个响应式对象回调收到新对象与旧对象。options的类型为OmitWatchOptionstrue, once——这是关键设计类型层面就移除了once字段杜绝调用方传入与内建语义冲突的配置。返回值WatchHandle与原生watch相同可通过调用返回的 stop 函数手动提前停止监听。关于MapSources/MapOldSources这两个辅助类型其定义位于 packages/shared/utils/types.tsexport type MapSourcesT { [K in keyof T]: T[K] extends WatchSourceinfer V ? V : never; } export type MapOldSourcesT, Immediate { [K in keyof T]: T[K] extends WatchSourceinfer V ? Immediate extends true ? V | undefined : V : never; }可以看到MapSourcesT将多数据源数组映射为各自值的元组类型MapOldSourcesT, Immediate则根据是否 immediate 决定旧值是否包含undefined联合。watchOnce的回调旧值类型走的是Immediate extends true分支T | undefined因为oncewatcher 的首次触发同样没有旧值。这些类型与 Vue 3 原生watch的重载保持一致保证了watchOnce在类型层面与watch无缝兼容。源码级实现剖析watchOnce的真实实现非常精简完整源码位于 packages/shared/watchOnce/index.tsimport type { MultiWatchSources, WatchCallback, WatchHandle, WatchOptions, WatchSource } from vue import type { MapOldSources, MapSources } from ../utils import { watch } from vue // overloads export function watchOnceT( source: WatchSourceT, cb: WatchCallbackT, T | undefined, options?: OmitWatchOptionstrue, once, ): WatchHandle export function watchOnceT extends ReadonlyMultiWatchSources( source: [...T], cb: WatchCallbackMapSourcesT, MapOldSourcesT, true, options?: OmitWatchOptionstrue, once, ): WatchHandle export function watchOnceT extends object( source: T, cb: WatchCallbackT, T | undefined, options?: OmitWatchOptionstrue, once, ): WatchHandle /** * Shorthand for watching value with { once: true } * * see https://vueuse.org/watchOnce */ export function watchOnceT any(source: T, cb: any, options?: OmitWatchOptions, once) { return watch( source as WatchSourceT, cb, { ...options, once: true, }, ) }实现要点透传并固定once函数体只做一件事——把调用方传入的options展开再强制并入once: true后转发给 Vue 的watch。这意味着「只触发一次并自动停止」的语义由 Vue 3 运行时保证VueUse 本身不额外维护停止逻辑。重载与实现分离三个export function声明只提供类型层面的重载签名最后一个宽松实现签名source: T, cb: any负责实际运行内部通过source as WatchSourceT做类型断言这是 VueUse 中一类简写 API 的常见写法。导出入口watchOnce由 packages/shared/index.ts 统一导出用户可通过import { watchOnce } from vueuse/core直接引入。值得强调的是once: true的行为是 Vue 3 原生特性在回调触发一次后watcher 的 effect 会自动停止stop此后不再响应任何数据变化也不需要手动调用返回的 stop 函数。测试验证回调确实只触发一次仓库为watchOnce提供了对应的单元测试 packages/shared/watchOnce/index.test.tsimport { describe, expect, it, vi } from vitest import { nextTick, shallowRef } from vue import { watchOnce } from ./index describe(watchOnce, () { it(should work, async () { const num shallowRef(0) const spy vi.fn() watchOnce(num, spy) num.value 1 await nextTick() num.value 2 await nextTick() expect(spy).toBeCalledTimes(1) }) })该测试精确刻画了watchOnce的契约先用shallowRef(0)创建监听源并用vi.fn()作为回调 spy第一次修改num.value 1并等待一个 tick回调应触发第二次修改num.value 2并等待一个 tick回调不应再次触发最终断言spy恰好被调用 1 次toBeCalledTimes(1)。这从测试角度实证了「回调触发一次后 watcher 即被停止」的核心行为。如果你在项目中遇到watchOnce回调被多次触发的疑问也可以参照此测试结构在本地用vitest复现验证仓库使用 Vitest配置见 vitest.config.ts。实战场景示例1. 等待异步状态首次就绪import { watchOnce } from vueuse/core import { ref } from vue const isReady ref(false) watchOnce(isReady, (val) { if (val) { // 状态首次变为就绪仅初始化一次 initOnce() } })注意watchOnce默认非 immediateisReady在监听建立时若已为true回调不会立即触发只会在其之后发生变化时触发一次。若希望当前值已满足即立即触发且只触发一次可结合immediate: true使用。2. 首次变化即停止的副作用import { watchOnce } from vueuse/core import { useRoute } from vue-router const route useRoute() watchOnce( () route.params.id, (id) { console.log(首次拿到路由参数:, id) }, )当路由参数第一次变化时执行一次记录之后监听自动失效避免后续多次导航反复触发。3. 组合 immediate 实现一次性初始化import { watchOnce } from vueuse/core import { ref } from vue const user refUser | null(null) // 用户信息一旦就位或已就位时立即触发只执行一次初始化 watchOnce(user, (val) { if (val) initDashboard(val) }, { immediate: true })由于options会原样透传给 Vue 的watch你可以自由组合immediate、deep、flush等选项让一次性监听适配更多初始化场景。注意事项与最佳实践once选项被类型层面禁用options类型为OmitWatchOptionstrue, once不要也无法在 TS 中传入once来试图覆盖。回调可收到undefined旧值首次触发时旧值为undefined与原生watch首次回调行为一致解构旧值前需留意。配合flush控制触发时机默认flush: pre组件更新前触发若需同步或后置触发可传入flush: sync/post与 Vue 原生watch完全一致。同类简写家族VueUse 还提供watchImmediate、watchDeep、watchDebounced、watchThrottled等 watch 系简写均可从vueuse/core导入它们遵循同样的透传 内建单选项设计哲学掌握watchOnce的实现后即可举一反三。总结watchOnce是 VueUse 中小而美的代表它以不到 10 行的实现将 Vue 3 的oncewatcher 封装成一个语义清晰、类型完备的 API让只响应一次变化的诉求从记得传选项 手动管理停止简化为一次函数调用。无论是等待异步状态、首次初始化还是任何只需要单次响应的场景watchOnce都是比手写watch更可读、更不易出错的选择。赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐Puppeteer CommonEventEmitter.once() 详解只触发一次的事件监听机制Puppeteer CommonEventEmitter.once 详解只触发一次的事件监听机制 本篇围绕 Puppeteer 事件体系中的 CommonEv浏览器控制测试网页爬虫开发工具airi 中的 VueUse watchOnce一次性监听器once watcher的原理、类型签名与工程实践airi 中的 VueUse watchOnce一次性监听器once watcher的原理、类型签名与工程实践 本篇技术指南围绕 airi 仓库中 .agAI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染VueUse watchOnceVue 3 一次性监听器实现原理与实战指南VueUse watchOnceVue 3 一次性监听器实现原理与实战指南 本文基于 VueUse 仓库 packages/shared/watchOnce/前端上一篇如何从Google AI Python SDK迁移到新版本完整迁移指南与最佳实践下一篇终极指南如何突破微信单设备限制WeChatPad实现多端登录新方法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表