- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
useArrayFindIndex是 VueUse(当前仓库gh_mirrors/vu/vueuse)在packages/shared包中提供的数组工具函数,它把 JavaScript 原生的Array.prototype.findIndex变成了一个响应式计算属性:当传入的数组元素(可以是普通值、ref或 getter)发生变化时,返回的下标会自动重算。阅读本文后,你将掌握它的两种典型用法、参数与返回值语义、底层源码实现原理,以及与useArrayFind、useArrayFindLast的取舍,从而在需要“找到第一个满足条件的元素下标”并跟随数据实时更新的场景中直接落地使用。
定位:响应式版本的Array.findIndex
原生Array.findIndex(callback)返回数组中第一个满足测试函数的元素下标,找不到时返回-1。问题在于它是一次性的:数据变化后你必须手动重新调用。useArrayFindIndex则返回一个ComputedRef<number>,底层由 Vue 的computed驱动,任何依赖变化都会自动触发重算,组件模板中可直接使用而无需手动同步。
核心实现位于 index.ts,完整类型签名如下:
export type UseArrayFindIndexReturn = ComputedRef<number> export function useArrayFindIndex<T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: MaybeRefOrGetter<T>[]) => unknown, ): UseArrayFindIndexReturn两个要点:
list既可以是「元素本身是 ref/getter 的数组」,也可以是「一个 ref 或 getter 返回数组」(即响应式数组),两种形态在下文分别演示;- 返回值恒为
ComputedRef<number>:命中返回首个满足条件元素的下标,未命中返回-1,与原生的findIndex语义一致。
用法一:数组由多个 ref 组成
文档 index.md 给出的第一种场景,是数组元素本身就是独立的ref:
import { useArrayFindIndex } 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 = useArrayFindIndex(list, i => i % 2 === 0) // result.value: 0 item1.value = 1 // result.value: 1执行过程拆解:
- 初始
list中 5 个元素全为偶数,第一个偶数下标为 0,所以result.value === 0; - 把
item1.value改为 1 后,数组变成[1, 2, 4, 6, 8],第一个偶数变成下标 1,result.value自动更新为 1; - 若所有元素都不满足条件,则返回
-1(见测试佐证一节)。
需要注意:这里的list是普通数组,但内部元素是ref。useArrayFindIndex在每次重算时通过toValue逐个解包元素,因此元素 ref 的变化依然能触发重新计算。
用法二:整个数组本身是响应式
第二种场景是「整个数组用一个ref包裹」,并对数组执行结构性修改:
import { useArrayFindIndex } from '@vueuse/core' const list = ref([0, 2, 4, 6, 8]) const result = useArrayFindIndex(list, i => i % 2 === 0) // result.value: 0 list.value.unshift(-1) // result.value: 1在ref([0, 2, 4, 6, 8])中,list是一个深度响应式数组。useArrayFindIndex先toValue(list)取出数组本身,再执行findIndex。由于数组是响应式的,无论是元素被修改、还是unshift/push/splice等结构性操作,都会触发依赖收集并自动重算:
- 初始数组中第一个偶数下标为 0;
unshift(-1)在头部插入 -1 后,数组变为[-1, 0, 2, 4, 6, 8],第一个偶数移到下标 1,结果随之更新。
这使它在“轮播图当前索引定位”“表单步骤定位”“第一个满足校验条件的字段下标”等动态场景中非常顺手。
源码原理:computed + toValue 的惰性响应
完整实现只有一行核心逻辑,位于 index.ts:
export function useArrayFindIndex<T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: MaybeRefOrGetter<T>[]) => unknown, ): UseArrayFindIndexReturn { return computed(() => toValue(list).findIndex((element, index, array) => fn(toValue(element), index, array))) }几个值得展开的底层细节:
computed保证惰性与缓存:函数返回的是ComputedRef<number>,只在依赖变化时才重算,模板或 watch 中读取result.value即可获得当前下标,无需任何手动订阅。toValue双层的解包:外层toValue(list)用于解包「整个数组」的 ref/getter;内层toValue(element)用于解包「数组元素」的 ref/getter,这与参数类型MaybeRefOrGetter<MaybeRefOrGetter<T>[]>一一对应。toValue是 Vue 3.3+ 提供的统一取值工具,同时兼容 ref 与 getter。- 回调三参数原样透传:
fn收到(element, index, array)三个参数,与原生findIndex一致。其中array是尚未解包元素的原始数组(类型为MaybeRefOrGetter<T>[]),element已被解包为真实值T,方便直接编写判断逻辑(如i => i % 2 === 0)。 @__NO_SIDE_EFFECTS__注解:源码顶部为该函数标注了无副作用标记,便于打包器做 tree-shaking 优化;@vueuse/shared包声明了"sideEffects": false(见 package.json),进一步保障了按需引入时的体积。
测试佐证:行为与边界情况
仓库为每个函数配备了 Vitest 单测,index.test.ts 覆盖了两种用法并验证了边界行为:
- 数组元素逐个变为不满足条件:依次把
item1~item5改为奇数,result.value从 0 依次前进到 4,最后全部不满足时返回-1—— 印证“找不到返回 -1”的原生语义; - 响应式数组的结构变化:
deepRef([0, 2, 4, 6, 8])配合unshift(-1)后下标从 0 变为 1 —— 印证数组结构性修改同样驱动重算。
该测试覆盖了文档中两个示例的行为,可作为你接入业务前理解预期结果的直接参考。
与兄弟函数对比:useArrayFind / useArrayFindLast
useArrayFindIndex属于 VueUse 的数组系列,在 packages/shared/index.ts 中统一导出,与以下两个函数形成互补:
| 函数 | 返回值 | 语义 | 源码位置 |
|---|---|---|---|
useArrayFind | ComputedRef<T \| undefined> | 返回第一个满足条件的元素本身,找不到返回undefined | useArrayFind/index.ts |
useArrayFindIndex | ComputedRef<number> | 返回第一个满足条件的下标,找不到返回-1 | useArrayFindIndex/index.ts |
useArrayFindLast | ComputedRef<T \| undefined> | 返回最后一个满足条件的元素(自后向前查找) | useArrayFindLast/index.ts |
选择建议:需要“元素值”用useArrayFind;需要“下标”用useArrayFindIndex;需要从尾部找用useArrayFindLast。值得一提的实现细节是,useArrayFindLast内部为 Node < 18 环境提供了手写的findLastpolyfill(见 useArrayFindLast/index.ts),而useArrayFindIndex依赖的原生findIndex在主流运行环境均已普及,因此实现上无需额外兼容代码。
获取与使用方式
useArrayFindIndex归属@vueuse/shared包,但按 VueUse 的约定,通过聚合包导入最方便:
import { useArrayFindIndex } from '@vueuse/core'当前仓库中@vueuse/shared版本为15.0.0,peerDependencies要求vue: ^3.5.0(见 package.json),因此请确保项目基于 Vue 3.5 及以上版本。由于@__NO_SIDE_EFFECTS__与sideEffects: false的双重保证,按需导入时多余代码会被安全摇树。
实战小结
把原生findIndex升级为响应式的三步心法:
- 确定数据形态:元素是独立 ref 就传普通数组,整个数组响应式就直接传
ref([...]),函数两者皆可; - 只写判断条件:回调
fn(element, index, array)只需关心真实元素值,解包与依赖追踪由computed + toValue完成; - 消费返回值:直接读取
result.value,或在模板中使用;找不到时得到-1,注意与useArrayFind的undefined区分。
在需要“跟随数据自动更新的第一个匹配下标”的场景中,useArrayFindIndex能省去大量手动watch同步代码,是数组系列工具里简洁且可靠的一员。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
VueUse 中的 useInfiniteScroll:为 Vue 3 打造响应式无限滚动加载
VueUse 中的 useInfiniteScroll:为 Vue 3 打造响应式无限滚动加载 本指南以 VueUse 仓库中 packages/core/us
前端VueUse @vueuse/math 之 logicNot:为 ref 提供响应式 NOT 逻辑判断的完整实战指南
VueUse @vueuse/math 之 logicNot:为 ref 提供响应式 NOT 逻辑判断的完整实战指南 logicNot 是 VueUse 数学扩
前端VueUse useArrayFilter 指南:在 Vue 3 中实现响应式的 Array.filter
VueUse useArrayFilter 指南:在 Vue 3 中实现响应式的 Array.filter useArrayFilter 是 VueUse 在
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考