
前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载computedAsync是 VueUse 响应式工具集中用于「异步计算」的核心 composable它像 Vue 内置computed一样声明式地跟踪依赖同时允许你传入返回 Promise 的异步求值函数并原生支持取消onCancel、求值状态追踪evaluating、懒加载lazy与错误处理onError。读完本文你将掌握在 Vue 3 / Nuxt 3 项目中用computedAsync替代手写watch ref的完整实战方案并深入理解其底层实现与边界限制。computedAsync 是什么在 Vue 3 中内置computed只能处理同步求值面对「依赖某个响应式值、却需要异步获取结果」的场景请求用户信息、查询下载量、拉取远程配置等开发者通常不得不手写watch搭配普通ref手动处理时序、竞态与取消逻辑。VueUse 的computedAsync正是为这一场景提供的声明式答案其文档定义只有一句话Computed for async functions为异步函数设计的计算属性。computedAsync属于 Reactivity 分类以 packages/core/index.ts 中的export * from ./computedAsync从vueuse/core统一导出同时保留旧别名asyncComputed已标记deprecated见 packages/core/computedAsync/index.ts。函数完整定义与文档位于 packages/core/computedAsync/index.ts 与 packages/core/computedAsync/index.md。基础用法三步接入异步计算computedAsync的基本签名是computedAsync(求值回调, 初始值, 选项)。第一个参数接收一个onCancel回调用于注册取消逻辑返回T | PromiseT第二个参数是首个求值完成前使用的初始状态。import { computedAsync } from vueuse/core import { shallowRef } from vue const name shallowRef(jack) const userInfo computedAsync( async () { return await mockLookUp(name.value) }, null, // initial state第一次求值完成前userInfo.value null )这段代码做了三件事建立依赖求值回调中同步读取了name.value因此name成为userInfo的依赖自动重求值name变化后computedAsync自动重新执行异步求值并更新结果提供过渡态在第一次请求返回前结果值保持为null。值得注意的是computedAsync默认非懒加载创建时立即开始第一次求值index.browser.test.ts 中it(is not lazy by default)验证了func在创建时即被调用一次这与 Vue 内置computed的懒求值行为不同。完整 API 与选项详解computedAsync的完整选项接口继承自ConfigurableFlushSync定义于 packages/shared/utils/types.ts并扩展了以下字段选项类型默认值作用lazybooleanfalse是否在首次访问.value时才触发求值evaluatingRefbooleanundefined传入一个 ref实时接收「是否正在求值」的状态shallowbooleantrue内部使用shallowRef浅层响应设为false改用深层refonError(e: unknown) voidglobalThis.reportError ?? noop异步求值抛出错误时的回调flushpre \| post \| syncsync依赖变化的监听时机继承自ConfigurableFlushSync此外第二个参数支持两种形态既可以是普通对象选项也可以直接传入一个Refboolean——此时它等价于{ evaluating: 该Ref }。源码中通过isRef(optionsOrRef)判断并归一化index.ts。flush 时机的特殊之处flush默认值为sync这与 Vue 原生watch默认的pre不同ConfigurableFlushSync的类型注释明确说明这一点。从实现看内部使用watchEffect(..., { flush })index.ts即依赖变化时同步触发重求值调度让异步计算结果能更及时地反映依赖变更。追踪求值状态Evaluation State当异步函数执行时间较长时UI 通常需要展示 loading 状态。computedAsync允许传入一个evaluatingref 来追踪「当前是否正在求值」既可作为第三个参数直接传入也可放进 options 中import { computedAsync } from vueuse/core import { shallowRef } from vue const evaluating shallowRef(false) const userInfo computedAsync( async () { /* your logic */ }, null, evaluating, // 等价于 { evaluating } 作为第三个参数 )// 或者通过 options 传入 const userInfo computedAsync( async () { /* your logic */ }, null, { evaluating }, )模板中即可直接用v-ifevaluating渲染加载提示。官方 demopackages/core/computedAsync/demo.vue正是用evaluating配合stringify(state)展示「Evaluating: true/false」状态并借id变化触发对 jsonplaceholder 接口的重新请求。从源码看evaluating的状态切换有两个实现细节index.ts求值开始时通过Promise.resolve().then(() evaluating.value true)延迟一拍赋值避免evaluating自身成为被追踪的依赖而引发循环求值结束成功或失败且该次求值仍是最新一次时在finally中把evaluating重置为falseindex.ts。取消过期请求onCancel 与 AbortController当依赖在异步函数 resolve 之前再次变化时旧请求已经过期继续等待它的结果会造成竞态。computedAsync的求值回调会注入一个onCancel函数用来注册「被新求值顶掉时」要执行的清理逻辑。官方示例展示了与 fetch 的AbortController配合的完整写法import { computedAsync } from vueuse/core import { shallowRef } from vue const packageName shallowRef(vueuse/core) const downloads computedAsync(async (onCancel) { const abortController new AbortController() onCancel(() abortController.abort()) return await fetch( https://api.npmjs.org/downloads/point/last-week/${packageName.value}, { signal: abortController.signal }, ) .then(response response.ok ? response.json() : { downloads: — }) .then(result result.downloads) }, 0)这里每次重新求值都会创建一个新的AbortController当packageName在上一次 fetch 完成前改变onCancel注册的abortController.abort()就会被调用从物理上终止过期网络请求避免旧结果覆盖新值。底层取消机制结合源码看取消是通过 Vue 的watchEffect副作用失效机制onInvalidate实现的index.ts用户在回调里调用onCancel(清理函数)VueUse 内部把它包装进onInvalidate当依赖变化触发重求值时上一次副作用被失效onInvalidate注册的清理函数被执行执行时会先把evaluating置为false且仅当上一次求值尚未结束!hasFinished时才调用用户的取消回调。AsyncComputedOnCancel的类型定义为(cancelCallback: Fn) void注释明确传入的回调会在「上一次求值尚未完成、新一次重求值已触发」时被调用index.ts。测试用例cancel is called与cancel is called for lazyindex.browser.test.ts验证了只有当旧求值未结束时依赖再次变化取消回调才会触发若上一次已 resolve则不会误触发取消。懒加载lazy 模式默认computedAsync在创建时立即开始求值。若希望「首次访问.value时才触发第一次求值」设置lazy: trueimport { computedAsync } from vueuse/core import { shallowRef } from vue const evaluating shallowRef(false) const userInfo computedAsync( async () { /* your logic */ }, null, { lazy: true, evaluating }, )源码中 lazy 的实现是内部维护startedref初始为!lazy非 lazy 时直接返回内部currentreflazy 时则包一层computed(() { started.value true; return current.value })一旦有人读取.value就置started为true并触发watchEffect开始求值index.ts。测试is lazy if configuredindex.browser.test.ts验证了lazy 模式下创建时func不被调用首次读取data.value后才触发。另外注意类型差异lazy 模式下返回ComputedRefT/ComputedRefT | undefined非 lazy 模式返回RefT/RefT | undefined测试types are correct when lazy与type when lazy is a boolean在 index.browser.test.ts 中通过expectTypeOf断言。错误处理onError异步求值抛出的异常默认不会冒泡破坏组件而是被捕获并交给onError。可自定义回调记录或上报错误import { computedAsync } from vueuse/core import { shallowRef } from vue const name shallowRef(jack) const userInfo computedAsync( async () { return await mockLookUp(name.value) }, null, { onError(e) { console.error(Failed to fetch user info, e) }, }, )实现上求值回调在try/catch中执行异常统一走onError(e)index.ts出错后current保持原值不变不会用异常污染结果。默认的onError是globalThis.reportError ?? noopindex.ts即优先调用 Web 平台的全局错误上报 API。测试用例验证了两点index.browser.test.ts默认行为错误会被转发给globalThis.reportError自定义行为传入onError后能收到原始错误对象并读取e.message此时结果值保持undefined。浅层 vs 深层响应shallow 选项computedAsync内部默认使用shallowRef保存结果即只对最外层引用做响应式处理。若你的异步结果本身是嵌套结构且需要深层响应例如模板深层访问对象的属性并希望其变更触发更新可设shallow: falseimport { computedAsync } from vueuse/core import { shallowRef } from vue const name shallowRef(jack) const userInfo computedAsync( async () { return await fetchNestedData(name.value) }, null, { shallow: false }, // enables deep reactivity )对应源码为const current (shallow ? shallowRef(initialState) : deepRef(initialState))index.ts其中deepRef即 Vue 的ref。默认浅层的主要收益是性能异步结果通常整体替换浅层 ref 避免了深层对象递归代理的开销。源码级原理并发求值的防竞态机制computedAsync最精巧的部分是只采用最新一次求值的结果。考虑一个典型竞态依赖快速连续变化 N 次会发起 N 个并发异步请求先发出的请求可能后返回。若直接覆盖旧请求会覆盖新结果。源码用递增计数器解决let counter 0 watchEffect(async (onInvalidate) { // ... counter const counterAtBeginning counter // ... try { const result await evaluationCallback(...) if (counterAtBeginning counter) current.value result // 仅当本次求值仍是最新一次才写入 } catch (e) { onError(e) } // ... })见 index.ts每次求值开始时记下当时的counterresolve 后对比counterAtBeginning counter才允许写入结果从而保证「过期请求永不覆盖最新结果」。测试uses last resultindex.browser.test.ts精确复现了这一场景依赖从 1 连续变为 2、3、4即使 2 与 3 对应的旧请求最后才 resolve最终值仍是依赖为 4 时的8。另外computedAsync的值是一个真正的响应式引用可以被其他computed继续派生。测试triggersindex.browser.test.ts展示了computed(() (double.value ?? 0) 1)会随异步结果更新而连锁刷新。注意事项Caveats官方文档明确列出两点与 Vue 内置computed的关键差异务必牢记只有第一层调用栈中同步访问的依赖才会触发重求值。computedAsync与 Vue 的computed一样做依赖追踪并在依赖变化时自动重算但异步阶段访问的依赖不会被追踪——例如await fetch(...)之后才读取的 ref其变化不会触发重求值。因此必须在回调开头第一层同步调用栈中同步读取所有依赖值。依赖变化即触发重求值与结果是否正被读取无关。与 Vue 内置computed相反内置 computed 只在结果被追踪时才求值computedAsync只要依赖变化就会重新求值无论其返回值当前是否被任何地方读取。这对「立即开始求值、结果常驻」的使用方式如预取数据更合适但也意味着它不会像内置 computed 那样按需懒求值——若需要按需求值请显式设置lazy: true。TypeScript 类型声明computedAsync有 4 个重载核心类型如下完整声明见 packages/core/computedAsync/index.tsexport type AsyncComputedOnCancel (cancelCallback: Fn) void export interface AsyncComputedOptionsLazy boolean extends ConfigurableFlushSync { /** 是否懒求值默认 false */ lazy?: Lazy /** 接收异步求值状态的 ref */ evaluating?: Refboolean /** 是否使用 shallowRef默认 true */ shallow?: boolean /** 捕获到错误时的回调 */ onError?: (e: unknown) void } computedAsyncT(evaluationCallback, initialState, optionsOrRef: AsyncComputedOptionstrue): ComputedRefT computedAsyncT(evaluationCallback, initialState: undefined, optionsOrRef: AsyncComputedOptionstrue): ComputedRefT | undefined computedAsyncT(evaluationCallback, initialState: T, optionsOrRef?: Refboolean | AsyncComputedOptions): RefT computedAsyncT(evaluationCallback, initialState?: undefined, optionsOrRef?: Refboolean | AsyncComputedOptions): RefT | undefined类型层面的返回值随lazy与是否提供initialState联动ComputedRefvsRef、TvsT | undefinedTypeScript 可以据此在编译期约束你的取值与空值判断。总结与最佳实践computedAsync把「依赖响应式状态 异步获取 竞态取消 状态追踪 错误兜底」封装成一个声明式原语取代了手写的watch样板代码。实战中建议遵循以下模式在求值回调第一行同步读取所有依赖的 ref确保依赖能被正确追踪涉及网络请求时始终用AbortControlleronCancel终止过期请求用evaluatingref 驱动 loading UI用onError收敛异常避免请求失败导致未捕获的 Promise 拒绝高频更新的异步结果保持默认shallow: true只有确实需要深层响应嵌套结果时才设置shallow: false若结果仅在特定时机才被消费用lazy: true延迟首次请求。如需进一步验证行为可直接阅读本仓库的 单元测试覆盖懒加载、竞态、取消、错误、类型推断等全部关键路径与 官方 demo完整展示evaluatingonCancel fetch 的组合用法。赞分享前端【免费下载链接】vueuseCollection of essential Vue Composition Utilities for Vue 3项目地址https://gitcode.com/gh_mirrors/vu/vueuse点击查看免费下载相关推荐airi 项目 Vue 异步计算实战VueUse computedAsync 的原理、用法与工程落地airi 项目 Vue 异步计算实战VueUse computedAsync 的原理、用法与工程落地 本篇指南以仓库 .agents/skills/vueusAI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染VueUse computedAsync 完全指南为 Vue 3 打造可靠的异步计算属性VueUse computedAsync 完全指南为 Vue 3 打造可靠的异步计算属性 computedAsync 是 VueUse 中用于处理异步计算的核前端VueUse useAsyncState 实战指南在 Vue 3 中以响应式方式管理异步状态VueUse useAsyncState 实战指南在 Vue 3 中以响应式方式管理异步状态 导读 useAsyncState 是 VueUse 核心包 前端上一篇Grommet终极指南5大核心功能构建现代化React应用下一篇React Native底部弹窗与React Navigation无缝集成终极指南打造流畅导航体验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考