news 2026/10/4 20:47:37

VueUse useArrayFindIndex:为 Vue 3 提供响应式的 Array.findIndex

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VueUse useArrayFindIndex:为 Vue 3 提供响应式的 Array.findIndex
  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

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

执行过程拆解:

  1. 初始list中 5 个元素全为偶数,第一个偶数下标为 0,所以result.value === 0;
  2. 把item1.value改为 1 后,数组变成[1, 2, 4, 6, 8],第一个偶数变成下标 1,result.value自动更新为 1;
  3. 若所有元素都不满足条件,则返回-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))) }

几个值得展开的底层细节:

  1. computed保证惰性与缓存:函数返回的是ComputedRef<number>,只在依赖变化时才重算,模板或 watch 中读取result.value即可获得当前下标,无需任何手动订阅。
  2. toValue双层的解包:外层toValue(list)用于解包「整个数组」的 ref/getter;内层toValue(element)用于解包「数组元素」的 ref/getter,这与参数类型MaybeRefOrGetter<MaybeRefOrGetter<T>[]>一一对应。toValue是 Vue 3.3+ 提供的统一取值工具,同时兼容 ref 与 getter。
  3. 回调三参数原样透传:fn收到(element, index, array)三个参数,与原生findIndex一致。其中array是尚未解包元素的原始数组(类型为MaybeRefOrGetter<T>[]),element已被解包为真实值T,方便直接编写判断逻辑(如i => i % 2 === 0)。
  4. @__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 中统一导出,与以下两个函数形成互补:

函数返回值语义源码位置
useArrayFindComputedRef<T \| undefined>返回第一个满足条件的元素本身,找不到返回undefineduseArrayFind/index.ts
useArrayFindIndexComputedRef<number>返回第一个满足条件的下标,找不到返回-1useArrayFindIndex/index.ts
useArrayFindLastComputedRef<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升级为响应式的三步心法:

  1. 确定数据形态:元素是独立 ref 就传普通数组,整个数组响应式就直接传ref([...]),函数两者皆可;
  2. 只写判断条件:回调fn(element, index, array)只需关心真实元素值,解包与依赖追踪由computed + toValue完成;
  3. 消费返回值:直接读取result.value,或在模板中使用;找不到时得到-1,注意与useArrayFind的undefined区分。

在需要“跟随数据自动更新的第一个匹配下标”的场景中,useArrayFindIndex能省去大量手动watch同步代码,是数组系列工具里简洁且可靠的一员。

  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

项目地址:https://gitcode.com/gh_mirrors/vu/vueuse
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 20:37:22

用触控板滑动窗口:Paneru滑动与滚轮操作完整指南

用触控板滑动窗口&#xff1a;Paneru滑动与滚轮操作完整指南 【免费下载链接】paneru A sliding, tiling window manager for MacOS. 项目地址: https://gitcode.com/gh_mirrors/pan/paneru Paneru 是一款为 macOS 打造的滑动式平铺窗口管理器&#xff0c;把窗口排列在一…

作者头像 李华
网站建设 2026/10/4 20:33:32

MR25H40CDF MRAM与PIC18LF46K80:工业不掉电存储的SPI读写方案

在做工业嵌入式设备的这些年间&#xff0c;数据存储始终是最让我头疼的环节之一。现场设备要掉电保存参数、要记录运行日志、要频繁更新配置&#xff0c;MR25H40CDF 这颗 SPI 接口的 MRAM 芯片配上 PIC18LF46K80 这颗带 CAN 控制器的 8 位单片机&#xff0c;把“掉电丢数据”和…

作者头像 李华
网站建设 2026/10/4 20:30:14

【A8 领域专业 Agent 落地】自动选品导购 Agent 组的多维度打分决策实战

【A8 领域专业 Agent 落地】自动选品导购 Agent 组的多维度打分决策实战在电商零售与数字化消费场景中&#xff0c;导购与选品一直处于业务价值链的核心交汇点。传统的推荐算法&#xff08;基于协同过滤、双塔模型或深度精排网络&#xff09;在过去十几年中统治了货架电商&…

作者头像 李华
网站建设 2026/10/4 20:15:00

Flask进销存系统实战:核心表结构、库存流水与并发控制

作为一个常年折腾后端业务系统的人&#xff0c;我接到过不少类似的单子&#xff1a;Python Flask 进销存。这看起来是个老生常谈的组合&#xff0c;但真正动手做的时候&#xff0c;才发现坑全藏在细节里。这次分享的项目是一个给某精品品牌企业用的进销存系统&#xff0c;品牌…

作者头像 李华