news 2026/10/5 4:47:26

VueUse useArrayFilter:响应式数组过滤的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VueUse useArrayFilter:响应式数组过滤的完整实战指南
  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

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

导读

useArrayFilter是 VueUse 核心包中一个轻量而强大的组合式函数,它将原生Array.prototype.filter变为响应式:当源数组或其元素发生变化时,过滤结果会自动重新计算并实时更新。本指南将基于官方文档与其在仓库中的源码、测试实现,带你完整掌握useArrayFilter的两种典型用法(元素为 ref 的数组、普通响应式数组)、其底层惰性求值原理、TypeScript 类型守卫能力,以及它与 VueUse 其他数组工具(useArrayMap、useArrayEvery等)的组合运用。

一、什么是 useArrayFilter

useArrayFilter是 VueUse 中 Reactive Array 系列工具之一,官方定位为"ReactiveArray.filter"。它接受一个数组(或其 ref / getter)和一个过滤回调函数fn,返回一个ComputedRef类型的结果:该结果始终是源数组中通过fn测试的那些元素组成的浅拷贝数组;如果没有元素通过测试,则返回空数组。

它的典型应用场景包括:

  • 基于多个独立响应式数据(如分页组件中的若干ref)动态生成筛选列表;
  • 对响应式数组(如用户输入、接口返回数据)进行实时过滤,无需手动监听变更;
  • 在模板中直接使用返回的 ref,自动驱动 UI 更新。

二、两种核心用法

原文档给出了两个最典型的调用模式,两者都保持完全相同的调用签名,区别只在于传入的源数组形态。

用法一:元素为多个 ref 的数组

import { useArrayFilter } 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 = useArrayFilter(list, i => i % 2 === 0) // result.value: [0, 2, 4, 6, 8] item2.value = 1 // result.value: [0, 4, 6, 8]

这是"Array of refs"模式:list本身是普通数组,但每个元素都是ref。useArrayFilter会在内部对每个元素再次调用toValue(i)解包,因此当任意元素(如item2)的值变化时,过滤结果会自动更新——这正是它区别于普通filter的关键。

用法二:元素为普通值的响应式数组

import { useArrayFilter } from '@vueuse/core' const list = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) const result = useArrayFilter(list, i => i % 2 === 0) // result.value: [0, 2, 4, 6, 8] list.value.shift() // result.value: [2, 4, 6, 8]

这是"reactive array"模式:整个数组装在一个ref中。当对数组执行shift()、push()、splice()等变更操作时,依赖追踪会触发重算,过滤结果同步变化。

两种模式可以自由混用:list既可以是MaybeRefOrGetter<T>[]形式(ref 元素数组),也可以整体是一个ref/ getter 指向的数组,因为第一层参数同样会被toValue(list)解包。

三、源码级原理解析

useArrayFilter的实现极其精炼,完整源码位于 packages/shared/useArrayFilter/index.ts,核心逻辑只有一行:

export function useArrayFilter<T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: T[]) => unknown, ): UseArrayFilterReturn<T> { return computed(() => toValue(list).map(i => toValue(i)).filter(fn)) }

1. 双重toValue解包

实现的核心是两层toValue解包:

  • 第一层toValue(list):把传入的数组(无论是普通数组、ref包裹的数组,还是 getter 函数)解包成真正的数组;
  • 第二层toValue(i):对数组中的每一个元素再次解包,从而支持"ref 元素数组"这种模式。

正是这种设计,让useArrayFilter同时兼容"元素为 ref 的数组"和"元素为普通值的响应式数组"两种形态。toValue是 Vue 3.3+ 提供的工具函数(等价于早期版本的unref通用化),也正因如此,MaybeRefOrGetter类型的值都能被优雅处理。

2. 惰性求值与响应式追踪

函数返回的是computed(() => ...),即一个ComputedRef<T[]>(类型别名UseArrayFilterReturn<T>)。这意味着:

  • 惰性求值:只有首次访问result.value时才执行过滤计算;
  • 自动依赖追踪:计算过程中访问到的所有响应式数据(list本身以及每个 ref 元素)都会被收集为依赖,任意依赖变化时结果自动重算;
  • 结果缓存:依赖未变化时重复读取result.value不会重复执行filter。

从源码结构看,这一实现模式与 useArrayMap 几乎一致(computed(() => toValue(list).map(i => toValue(i)).map(fn))),与 VueUse 整个 Reactive Array 系列工具保持统一的架构风格。

3. TypeScript 重载与类型守卫

useArrayFilter提供了两个函数重载,其中一个支持类型守卫(type guard)用法:

export function useArrayFilter<T, S extends T>( list: MaybeRefOrGetter<MaybeRefOrGetter<T>[]>, fn: (element: T, index: number, array: T[]) => element is S, ): UseArrayFilterReturn<S>

当你的过滤函数写成(item): item is SomeType => ...这种谓词形式时,返回类型会自动收窄为S[],让后续代码获得完整的类型推断。这在过滤联合类型(如(string | null)[])时非常有用:

const list = ref<(string | null)[]>(['a', null, 'b']) const result = useArrayFilter(list, (i): i is string => i !== null) // result.value 的类型被推断为 string[]

四、测试用例验证

仓库在 packages/shared/useArrayFilter/index.test.ts 中为useArrayFilter提供了三个 vitest 测试用例,可以作为行为契约参考:

  1. 与 ref 数组协同:用shallowRef构造元素数组,断言初始结果为[0, 2, 4, 6, 8],修改item2.value = 1后结果为[0, 4, 6, 8]——验证了元素级响应式;
  2. 与响应式数组协同:用deepRef包裹[0..9],断言过滤结果,再执行list.value.shift(),结果变为[2, 4, 6, 8]——验证了数组级变更的响应式;
  3. 回调返回值不限于布尔:useArrayFilter(list, i => i % 2)能返回[1, 3, 5, 7, 9]——验证了过滤语义与原生filter一致(采用真值测试,任何 truthy 返回值都会被保留)。

第三点值得注意:fn的返回值并不强制是boolean,只要返回真值即可保留元素,这与原生Array.filter的语义完全一致。

五、参数说明与边界行为

参数类型说明
listMaybeRefOrGetter<MaybeRefOrGetter<T>[]>被过滤的源数组,可以是普通数组、ref/shallowRef包裹的数组,或返回数组的 getter 函数;数组元素也可以是 ref
fn(element: T, index: number, array: T[]) => unknown对每个元素执行的测试函数,接收当前元素、索引和完整数组(解包后),返回真值表示保留该元素

返回值类型为ComputedRef<T[]>(对应类型别名UseArrayFilterReturn<T>),即"通过测试的源数组元素的浅拷贝";若没有元素通过测试,返回空数组。

需要注意的边界行为:

  • 惰性计算:过滤逻辑不会在调用时立刻执行,而是在首次读取.value时执行;因此如果从未读取结果,修改源数组不会产生任何计算开销;
  • 每次重算执行完整map+filter:依赖变化时会重新解包整个数组并重跑过滤,对于超大规模数组应评估其性能;对于超大列表场景,可考虑配合useDebounceFn或派生工具做节流/防抖;
  • 副作用函数请谨慎:fn在每次重算时都会被调用,不要在fn中放置有副作用的操作,这符合原生filter的纯函数预期。

六、组合与相关函数家族

useArrayFilter属于 VueUse Shared 包中完整的 Reactive Array 工具家族,它们共享同一套"双解包 + computed"架构,全部从 packages/shared/index.ts 统一导出,可组合使用:

  • useArrayMap:响应式Array.map,与useArrayFilter配合可组成"过滤 → 映射"的数据管道;
  • useArrayEvery:响应式Array.every;
  • useArraySome:响应式Array.some;
  • useArrayIncludes:响应式Array.includes;
  • useArrayUnique:数组去重。

一个典型的数据管道示例:

import { useArrayFilter, useArrayMap } from '@vueuse/core' const items = ref([ { name: 'Apple', price: 5, stock: 3 }, { name: 'Banana', price: 2, stock: 0 }, { name: 'Cherry', price: 4, stock: 10 }, ]) // 筛选有库存的商品,并映射出名称列表 const inStockNames = useArrayMap( useArrayFilter(items, i => i.stock > 0), i => i.name, ) // inStockNames.value: ['Apple', 'Cherry']

由于useArrayFilter返回的是ComputedRef,而useArrayMap第一参数同样接受MaybeRefOrGetter数组,两者可以无缝串联,形成完全响应式的数据转换链。

七、总结

useArrayFilter用一行核心计算逻辑(toValue双解包 +computed)就为 Vue 3 应用带来了原生Array.filter的完整响应式能力:

  • 入参灵活:支持 ref 元素数组、响应式数组、getter,甚至二者的混合形态;
  • 结果自动更新:任何源数据变化都会触发过滤结果重算,模板中使用result即可实时渲染;
  • 类型安全:通过函数重载支持类型守卫,过滤后类型自动收窄;
  • 与生态协同:可与useArrayMap等 Reactive Array 家族成员自由组合成响应式数据管道。

无论是构建筛选面板、搜索列表还是实时数据仪表盘,useArrayFilter都能以极低的代码成本完成任务。它的完整实现、类型定义与测试用例都值得在 源码目录 中进一步研读。

  • 前端

【免费下载链接】vueuse

Collection of essential Vue Composition Utilities for Vue 3

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

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

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

LangGraph子图模式实战:多智能体AI客服架构设计与实现

1. 为什么我最终选了子图模式来做 AI 客服1.1 从单智能体到多智能体的真实痛点去年下半年我接了一个 AI 客服系统的项目&#xff0c;客户是做 SaaS 的&#xff0c;每天大概有三千到五千条用户咨询&#xff0c;内容涵盖账单问题、功能使用、账号异常、退款流程这几大类。一开始我…

作者头像 李华
网站建设 2026/10/5 4:47:21

DeepSeek Harness桌面端实战:API Key配置、插件管理与内网部署

1. 桌面端来了&#xff0c;为什么这件事比想象中重要DeepSeek Harness 出官方桌面端这件事&#xff0c;我第一反应不是"终于有 GUI 了"&#xff0c;而是"终于不用再跟终端里的环境变量死磕了"。如果你最近一直在用命令行版本的 DSH&#xff0c;应该懂我在说…

作者头像 李华
网站建设 2026/10/5 4:47:04

手势识别目标检测实战:2400张YOLO数据集全解析

简介&#xff1a;面向目标检测与手势识别任务的数据集&#xff0c;采用YOLO与VOC两种标注格式&#xff0c;涵盖fist、no_gesture、like、ok、palm五个手势类别&#xff0c;共2400张图片&#xff0c;适合用于YOLOv5至YOLOv10、Faster RCNN、SSD等模型的训练与算法对比。资源内除…

作者头像 李华
网站建设 2026/10/5 4:46:18

AI Native 团队开发手册:从 CLAUDE.md 到多 Agent 编排的 SDLC 重构

1. 从“堆人”到“造系统”&#xff1a;AI Native 团队到底在变什么这两年带团队做研发&#xff0c;最直观的感受就是&#xff1a;以前一个需求从评审到上线&#xff0c;中间要经过产品、设计、前端、后端、测试、运维至少六七个角色接力&#xff0c;一个中等复杂度的功能排期两…

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

DeepAgents+MCP+A2A+Skills:多Agent集群架构设计与工程实践

1. 从单体到集群&#xff1a;为什么我们需要重新理解 Agent 架构过去一年我一直在做 Agent 相关的项目&#xff0c;从最早的单一对话机器人&#xff0c;到后来带工具调用的 ReAct 循环&#xff0c;再到多角色协作的流水线&#xff0c;踩过的坑可以说能写一本书。但真正让我意识…

作者头像 李华
网站建设 2026/10/5 4:44:59

ABAP OO方法参数:引用与值传递的实务指南

这个标题看着像语法科普&#xff0c;其实做 ABAP 时间长了你会发现&#xff0c;它直接关系到你维护老程序时的心态。方法参数这几个关键字&#xff0c;IMPORTING、EXPORTING、CHANGING、RETURNING&#xff0c;再加上 VALUE 和 REFERENCE 这对隐藏语义&#xff0c;很多同事写了两…

作者头像 李华