深入解析 airi 项目中的 VueUse useArrayEvery:响应式 Array.every 的完整指南
【免费下载链接】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-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi
导读
useArrayEvery是 VueUse 响应式工具集(Array 分类)中的一员,本质上是将原生Array.prototype.every包装为响应式版本:当数组或其元素发生变化时,计算结果会自动重算。在 airi 这样的 Web / macOS / Windows 跨端 Vue 3 应用中,它常用于"全部条件同时满足"类校验——例如表单整体可提交判断、性能指标全部启用校验、多包币种全部可用检查。读完本文你将掌握:useArrayEvery的两种典型调用形态(多个 ref 构成的数组、响应式数组)、完整的类型签名解读、在 airi 仓库中的实际落地场景,以及与其姊妹函数(useArraySome、useArrayFilter等)的选型对比。
一、从 VueUse 到 airi:这个函数的项目背景
airi 是一个自托管的 AI Companion 桌面/Web 项目(Web / macOS / Windows 支持),其前端全部基于 Vue 3 构建,并通过 pnpm workspace 统一管理依赖。仓库的 pnpm-workspace.yaml 中以 catalog 方式声明了核心版本:
'@vueuse/core': ^14.4.0 '@vueuse/shared': ^14.4.0useArrayEvery就来自@vueuse/core(同时依赖@vueuse/shared的基础类型与工具函数)。在 airi 的多个子应用与包中均直接依赖@vueuse/core,例如 apps/stage-web/package.json、apps/stage-pocket/package.json、apps/stage-tamagotchi/package.json、packages/stage-pages/package.json、packages/electron-vueuse/package.json 等,因此useArrayEvery在整个 airi 代码库中开箱即用。
此外,仓库内置的 AI 编码技能 .agents/skills/vueuse-functions/SKILL.md 将 VueUse 函数按功能分类(State、Elements、Array、Reactivity 等),其中Array 分类明确收录了useArrayEvery,描述为 "ReactiveArray.every",并标注Invocation: AUTO——即在 Vue.js / Nuxt 开发中,只要需求可映射到该函数就应优先使用,而不是手写监听逻辑。本篇文章讲解的参考文档正是该技能目录下的 .agents/skills/vueuse-functions/references/useArrayEvery.md,该文档由vendor/vueuse/skills/vueuse-functions同步而来(见 .agents/skills/vueuse-functions/SYNC.md),是标准的 VueUse 官方函数参考。
适用前提:需要 Vue 3(或以上)/ Nuxt 3(或以上)项目,并已安装
@vueuse/core(airi 各前端应用均满足)。
二、核心概念:什么是"响应式 Array.every"
原生 JavaScript 的Array.prototype.every(callback)会遍历数组,当每一个元素都让回调函数返回真值时返回true,否则立即短路返回false。但它是一次性计算,数组变化后不会自动更新。
useArrayEvery把这一语义搬进 Vue 的响应式系统:它接受一个可响应的数组(数组本身可以是ref/getter,数组元素也可以是ref/getter),返回一个ComputedRef<boolean>。任何相关依赖(数组引用、元素值)发生变化时,计算结果都会自动重新求值,无需手动watch+ 赋值。
从 useArrayEvery.md 给出的类型声明可以清晰看到设计:
export type UseArrayEveryReturn = ComputedRef<boolean> export declare function useArrayEvery<T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: MaybeRefOrGetter<T>[]) => unknown, ): UseArrayEveryReturn逐项解读:
list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>:第一层MaybeRefOrGetter表示整个数组可以是普通数组、Ref<Array>或返回数组的 getter;第二层MaybeRefOrGetter<T>表示每个元素也允许是普通值、Ref<T>或返回T的 getter。这对应文档中两种官方用法(见下文第三、四节)。fn: (element, index, array) => unknown:测试回调,签名与原生Array.prototype.every一致,接收元素、下标与整个数组;返回值只需为 truthy/falsy 即可,不必是布尔值。- 返回值
ComputedRef<boolean>:当fn对数组每一个元素都返回 truthy 时为true,否则为false;且标注了@__NO_SIDE_EFFECTS__,即该组合式函数无副作用,可安全用于 SSR/组合式函数上下文中。
实现推断:从返回类型是
ComputedRef<boolean>可以看出,其内部必然基于computed构建,并在求值过程中先对传入的 list 解包(toValue),再对每个元素逐一解包后调用fn做every短路判断——这与 VueUse 中useArrayFilter、useArraySome等"响应式数组变换"家族的实现模式一致(同属 Array 分类,见 SKILL.md 中的 Array 函数表)。
三、用法一:与多个 ref 构成的数组搭配
这是文档给出的第一种官方用法,适用于元素各自是独立 ref的场景。以文档示例为基础:
import { useArrayEvery } from '@vueuse/core' const item1 = ref(0) const item2 = ref(2) const item3 = ref(4) const item4 = ref(6) const item5 = ref(8) const list = [item1, item2, item3, item4, item5] const result = useArrayEvery(list, i => i % 2 === 0) // result.value: true item1.value = 1 // result.value: false执行过程推演:
- 初始时数组元素为
0, 2, 4, 6, 8,全部是偶数,i % 2 === 0对每个元素都成立,因此result.value === true; - 当
item1.value = 1时,数组第一项变为奇数,every语义下不再"全部通过",结果立即变为false。
这种形态的关键价值在于:被追踪的依赖是元素 ref 本身,而不是数组的引用。即使list数组变量本身从未被重新赋值,只要其中任意一个ref的值改变,result都会响应式更新。
四、用法二:与响应式数组(ref array)搭配
第二种官方用法面向"整组数据都在一个 ref 数组里"的场景:
import { useArrayEvery } from '@vueuse/core' const list = ref([0, 2, 4, 6, 8]) const result = useArrayEvery(list, i => i % 2 === 0) // result.value: true list.value.push(9) // result.value: false这里list是一个Ref<number[]>。调用push(9)之后数组包含奇数 9,result.value自动变为false。注意:Vue 3 的ref对数组的响应式是基于 Proxy 的深层响应式,因此push、splice、下标赋值等原地变更都能被computed依赖收集捕捉到,这正是useArrayEvery能感知"数组内容变化"的底层机制。
对比两种用法可归纳出选型建议:
| 场景 | 推荐形态 |
|---|---|
| 元素是独立的 ref,分散在不同状态中 | 多个 ref 构成的数组(用法一) |
| 数据天然集中在一个响应式数组中(如列表、批量配置) | ref([...])响应式数组(用法二) |
两种形态均无需手写watch、无需computed(() => list.value.every(...))之外的心智负担,且都保持ComputedRef<boolean>的返回值协议,可在模板中直接v-if="result"或在组合式函数中继续派生。
五、在 airi 仓库中的真实应用场景
虽然useArrayEvery本体的源码位于@vueuse/core依赖中(node_modules),但 airi 代码库中存在大量与其语义一致的"全量校验"逻辑,可以直接替换为useArrayEvery以收敛样板代码。下面两处是典型代表:
场景 A:性能指标全部启用判定
在 apps/stage-web/src/pages/devtools/performance-visualizer.vue 中,出现了对指标启用状态的批量校验:
supportedMetrics.every(metric => enabled.value[metric.key])这里的意图是"所有支持的指标都已启用才……"。supportedMetrics与enabled都是响应式来源,正是useArrayEvery(supportedMetrics, metric => enabled.value[metric.key])的天然替换点——替换后可获得ComputedRef<boolean>,并随enabled状态自动更新。
场景 B:多币种包全部可用检查
在 packages/stage-pages/src/pages/settings/flux.vue 中:
.filter(c => packages.value.every(p => c in p.currencies))遍历候选货币,过滤出"每个包(packages)的 currencies 中都包含该货币"的项。内层packages.value.every(...)判断的正是"全部包都支持",与useArrayEvery(packages, p => c in p.currencies)语义一致。
这类"全部满足"判定在 airi 的桌面端(stage-tamagotchi)、Web 端(stage-web)、移动端(stage-pocket)乃至 stage-pages 包中普遍存在,统一收敛到useArrayEvery后,代码可读性、可维护性与响应式正确性都能得到提升。
六、选型对比:useArrayEvery 与相关函数
useArrayEvery属于 VueUse 的Array 分类。在 SKILL.md 的 Array 表中,与之相邻的函数包括useArraySome、useArrayFilter、useArrayFind、useArrayMap、useArrayIncludes、useArrayJoin、useArrayReduce、useArrayUnique、useArrayDifference、useSorted,它们全部标注为AUTO 调用。选型要点:
| 函数 | 语义 | 适用场景 |
|---|---|---|
useArrayEvery | 全部元素通过测试 →true | 全量条件校验(本文主题) |
useArraySome | 任一元素通过测试 →true | 至少一个满足(如"存在启用中的指标") |
useArrayFilter | 过滤出通过测试的元素 | 保留满足条件的子集 |
useArrayFind | 返回第一个通过测试的元素 | 查找目标项 |
useArrayIncludes | 判断是否包含指定值 | 成员存在性判断 |
useArrayMap/useArrayReduce | 映射 / 归约 | 派生新数组或聚合值 |
决策原则(来自 SKILL.md 的 When to Apply):在 Vue.js / Nuxt 开发中先检查 VueUse 是否已有对应函数;优先用组合式函数替代自造代码;Invocation 为AUTO的(如useArrayEvery)在适用时直接使用,无需征求用户额外确认。换言之:需要"全部满足"判断时,useArrayEvery就是首选答案。
七、常见问题与注意事项
- 返回值始终是
ComputedRef<boolean>:读取需.value(模板中自动解包),不要把它当作普通布尔值直接比较。 - 回调返回 truthy 即可:
fn类型为(element, index, array) => unknown,不强制返回严格boolean,与原生every一致。 - 对空数组的语义:
every对空数组天然返回true(数学上的"真空真"),useArrayEvery继承该语义,涉及空数组判定时需自行注意。 - 深层响应式依赖:用法二中
ref([...])依赖 Vue 3 的 Proxy 深层响应式,原地修改(push/下标赋值)才会被追踪;若替换整个数组引用(list.value = [...])同样有效。 - 版本与运行环境:airi 当前 catalog 版本为
@vueuse/core ^14.4.0(见 pnpm-workspace.yaml),该函数要求 Vue 3(或以上),适合 airi 的 Web / macOS / Windows 三端 Vue 3 工程。
小结
useArrayEvery是"把原生Array.every接入 Vue 响应式"的最短路径:一条computed语义、零样板代码、两种输入形态,返回值类型明确(ComputedRef<boolean>),并由 airi 内置的 VueUse 技能(SKILL.md)标记为 AUTO 优先调用。无论是多 ref 分散状态(用法一)还是集中式响应式数组(用法二),它都能让"全部条件同时成立"这类判断随状态自动保鲜。对照 airi 中 performance-visualizer.vue 与 flux.vue 的既有every用法,你可以快速将这类模式收敛为响应式组合式函数,让代码更简洁、更可维护。
【免费下载链接】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-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考