- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
useArrayFindLast 是 VueUse 提供的一组响应式数组工具函数之一,它把原生Array.prototype.findLast封装成了基于computed的响应式派生数据:只要传入的数组或回调依赖发生变化,返回的ComputedRef就会自动重新计算。本文以 packages/shared/useArrayFindLast/index.md 为核心,结合仓库源码 index.ts 与测试用例 index.test.ts,完整讲解它的用法、参数、底层实现原理与常见坑位,读完即可在自己的 Vue 3 项目中直接落地使用。
一句话概括
useArrayFindLast(list, fn)返回一个只读的计算属性(ComputedRef),其值始终等于数组中最后一个满足测试函数fn的元素;如果没有任何元素满足条件,则值为undefined。它的行为与原生Array.findLast完全一致,区别在于它是响应式的——数组本身或其元素的变化都会被自动追踪并触发重新求值。
基础用法
首先从@vueuse/core中导入函数:
import { useArrayFindLast } from '@vueuse/core'最常见的用法是传入一个由ref组成的数组,配合一个测试回调:
const list = [ref(1), ref(-1), ref(2)] const positive = useArrayFindLast(list, val => val > 0) // positive.value: 2这里数组list中的元素是ref(1)、ref(-1)、ref(2),回调函数val => val > 0用于判断元素是否为正数。由于findLast是从数组末尾向前搜索,因此返回的是最后一个正数2,而不是第一个正数1。
返回值是ComputedRef<T | undefined>类型的只读 ref,在模板或watch中可以直接使用:
<script setup lang="ts"> import { ref } from 'vue' import { useArrayFindLast } from '@vueuse/core' const list = [ref(1), ref(-1), ref(2)] const positive = useArrayFindLast(list, val => val > 0) </script> <template> <p>最后一个正数是:{{ positive }}</p> </template>回调签名
回调函数的签名与原生Array.prototype.findLast一致,接收三个参数(源码见 index.ts):
| 参数 | 类型 | 说明 |
|---|---|---|
element | T | 当前正在测试的元素(已自动解包 ref,见下文"元素自动解包") |
index | number | 当前元素的索引,从数组末尾向前的搜索过程中索引依次递减 |
array | MaybeRefOrGetter<T>[] | 被搜索的数组本身 |
const result = useArrayFindLast(list, (element, index, array) => { console.log(element, index, array.length) return element > 0 })与 useArrayFind 的差异
VueUse 同系列还提供useArrayFind(返回第一个匹配元素)与useArrayFindIndex(返回第一个匹配元素的索引,找不到时返回-1)。三者对比如下:
| 函数 | 行为 | 找不到时返回 |
|---|---|---|
useArrayFind | 从头开始找第一个匹配元素 | undefined |
useArrayFindLast | 从尾开始找最后一个匹配元素 | undefined |
useArrayFindIndex | 从头开始找第一个匹配元素的索引 | -1 |
例如同样的数组[ref(1), ref(-1), ref(2)]与回调val => val > 0,useArrayFind返回1,而useArrayFindLast返回2。对应实现可参考 useArrayFind/index.ts 与 useArrayFindIndex/index.ts。
与响应式数组配合使用
useArrayFindLast的强大之处在于,即使传入的是reactive数组(或其长度动态变化),结果也会实时更新。文档中的示例:
import { useArrayFindLast } from '@vueuse/core' const list = reactive([-1, -2]) const positive = useArrayFindLast(list, val => val > 0) // positive.value: undefined list.push(10) // positive.value: 10 list.push(5) // positive.value: 5逐步解读:
- 初始数组
[-1, -2]中没有正数,因此positive.value为undefined; list.push(10)后,数组变为[-1, -2, 10],最后一个正数是10;- 继续
list.push(5)后,数组变为[-1, -2, 10, 5],此时最后一个正数变成5(因为5在10之后)。
这个行为在测试用例 index.test.ts 中被完整验证:
it('should work with reactive array', () => { useSetup(() => { const list = reactive([-1, -2]) const positive = useArrayFindLast(list, val => val > 0) expect(positive.value).toBe(undefined) list.push(10) expect(positive.value).toBe(10) list.push(5) expect(positive.value).toBe(5) }) })也就是说:数组内容变化(增删元素、元素值变化)都会让计算结果自动刷新,这是它相对于手动调用findLast的核心价值——你不需要自己去监听数组并手动更新状态。
参数类型详解
从源码的类型签名(index.ts)可以看出两个参数的完整类型:
export function useArrayFindLast<T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: MaybeRefOrGetter<T>[]) => boolean, ): UseArrayFindLastReturn<T>其中:
list:类型为MaybeRefOrGetter<MaybeRefOrGetter<T>[]>。这意味着它可以接受:- 普通数组:
[1, 2, 3]; - 元素为
ref的数组:[ref(1), ref(2)]; - 元素为
getter函数的数组:[() => 1, () => 2]; - 由
ref包裹的数组:ref([1, 2, 3]); - 由
getter函数返回的数组:() => [1, 2, 3]。
- 普通数组:
fn:测试回调,返回boolean,决定元素是否"匹配"。- 返回值:
UseArrayFindLastReturn<T>,即ComputedRef<T | undefined>。
元素自动解包
有一个容易被忽略的细节:当数组元素是ref时,回调函数接收到的element参数已经被自动解包(unwrapped)。源码中通过toValue(element)完成了这一操作:
return computed(() => toValue<T | undefined>( !Array.prototype.findLast ? findLast(toValue(list), (element, index, array) => fn(toValue(element), index, array)) : toValue(list) .findLast((element, index, array) => fn(toValue(element), index, array)), ))因此你不需要在回调里手动写val.value,直接写val => val > 0即可,即使数组元素是ref。
源码原理:computed 封装与旧环境 Polyfill
useArrayFindLast的完整实现只有约 20 行(index.ts),核心逻辑可以拆解为三层:
1. 响应式外层:computed
函数整体由computed()包裹,toValue(list)会在求值阶段取出当前数组。由于 Vue 的computed会自动追踪求值过程中读取到的所有响应式依赖,因此:
- 数组本身是
ref/reactive时,数组的增删与替换会被追踪; - 数组元素是
ref/getter时,读取元素的过程(toValue(element))也会被追踪。
任一依赖变化,都会触发重新计算。这也是"响应式"的来源。
2. 功能主体:原生 findLast 优先
实现优先使用原生Array.prototype.findLast(ECMAScript 2023 规范,Chrome 97+、Node.js 18+ 等现代环境已支持):
toValue(list) .findLast((element, index, array) => fn(toValue(element), index, array))注意这里回调的element同样经过toValue(element)解包,所以fn中拿到的是解包后的值。
3. 兜底方案:内置 Polyfill
对于不支持findLast的旧环境(如 Node.js < 18),源码内置了一个手动实现的findLastPolyfill(index.ts):
// Polyfill for node version < 18 function findLast<T>(arr: T[], cb: (element: T, index: number, array: T[]) => boolean): T | undefined { let index = arr.length while (index-- > 0) { if (cb(arr[index], index, arr)) return arr[index] } return undefined }其算法是从数组末尾向前遍历(index从arr.length递减到0),找到第一个满足回调的元素立即返回;全部不满足则返回undefined。选择逻辑通过!Array.prototype.findLast判断:只有环境缺失该原生方法时才走 Polyfill,现代环境下零额外开销。
导出与体积
useArrayFindLast经由 packages/shared/index.ts 统一导出,随@vueuse/core一起发布。根据仓库中的 packages/export-size.json 记录,其打包后体积约为199 B(gzip 前,按项目 export-size 统计口径),非常轻量。
响应式失效演示:元素 ref 变化也会触发更新
除了reactive数组,元素级ref的变化同样会被追踪。测试用例 index.test.ts 演示了这一场景:
it('should find positive', () => { useSetup(() => { const item1 = shallowRef(1) const item2 = shallowRef(2) const item3 = shallowRef(3) const positive = useArrayFindLast([item1, item2, item3], val => val > 0) expect(positive.value).toBe(3) item3.value = -1 expect(positive.value).toBe(2) item2.value = -1 expect(positive.value).toBe(1) item1.value = -1 expect(positive.value).toBe(undefined) }) })执行过程:
- 初始
[1, 2, 3],最后一个正数为3; item3变为-1,数组变为[1, 2, -1],最后一个正数变为2;item2变为-1,数组变为[1, -1, -1],最后一个正数变为1;item1变为-1,数组变为[-1, -1, -1],无正数,结果为undefined。
这说明即使数组本身不变,只要某个元素的ref值变化,useArrayFindLast的结果也会自动更新——非常适合在列表项状态频繁变动的场景(如待办事项的完成状态、选中状态判断)中直接使用。
常见场景建议
结合以上特性,useArrayFindLast适合以下实战场景:
- 取"最后一个满足条件"的条目:如判断一组校验规则中最后一个未通过校验的字段、获取列表中最后一个处于某状态的节点;
- 与 ref 元素数组搭配:当元素本身是响应式状态且会被单独更新时,无需手动重组数组;
- 与响应式数组搭配:当数组长度与内容会动态变化(如
push、splice)时,结果自动跟随刷新; - 替代手动 watch:相比
watch(list, () => { ... })后再手动调用findLast,直接使用本函数更简洁,且天然惰性求值(仅在读取.value时计算)。
需要从数组头部找第一个匹配元素时,改用 useArrayFind;需要拿到匹配元素的索引时,改用 useArrayFindIndex。三者共享相同的响应式设计:都基于computed+toValue,同样支持ref数组、reactive数组与元素自动解包。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
StarRocks cardinality() 函数详解:获取数组与 Map 元素个数的权威指南
StarRocks cardinality 函数详解:获取数组与 Map 元素个数的权威指南 cardinality 是 StarRocks 中用于返回数组(A
数据库OLAP数据仓库大数据湖仓一体数据分析Enzyme ReactWrapper `.last()` 方法详解:取匹配节点集合的最后一个元素
Enzyme ReactWrapper .last 方法详解:取匹配节点集合的最后一个元素 .last 是 Enzyme 测试工具中用于将当前 wrapper
测试前端30-seconds-of-code:用数组解构提取 JavaScript 数组最后一个元素
30 seconds of code:用数组解构提取 JavaScript 数组最后一个元素 数组解构(destructuring assignment)是 E
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考