尧图精选

TanStack Table (Octane) 模糊过滤实战指南:基于 match-sorter-utils 的近似匹配、全局过滤与排名排序

🕒 发布时间:2026/9/20 4:45:41 📁 来源:尧图网络
TanStack Table (Octane) 模糊过滤实战指南基于 match-sorter-utils 的近似匹配、全局过滤与排名排序【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table本篇指南讲解如何在tanstack/octane-tableTanStack Table v9 的 Octane 适配层中实现模糊过滤Fuzzy Filtering通过tanstack/match-sorter-utils提供的rankItem计算近似匹配排名把自定义的fuzzy过滤/排序函数注册到tableFeatures的filterFns与sortFns插槽中并分别应用于全局过滤与列过滤两种场景最终实现搜索结果按匹配程度排序的完整体验。读完本文你将能独立为 Octane 表格接入可复制的模糊搜索、基于排名的排序并理解底层 row-model 与 filter meta 的协作机制。一、什么是模糊过滤模糊过滤Fuzzy Filtering是一种基于近似匹配来筛选数据的技术与要求精确相等或包含的普通过滤不同它允许用户输入与目标值相似但不完全一致的查询词例如输入jane doe也能命中Jane Doe输入tanstak也能匹配TanStack。当数据量很大、用户记不清精确拼写时这种容错搜索能显著改善体验。在 TanStack Table 中模糊过滤通常与全局过滤global filtering搭配使用搜索框跨所有列但也可以应用到单个列如全文拼接列fullName。本文两种用法都会覆盖。要使用模糊过滤你需要安装独立的工具库pnpm add tanstack/match-sorter-utils[!NOTE]tanstack/match-sorter-utils是 match-sorter作者 Kent C. Dodds的 TanStack 分支版本fork 的目的是更好地适配 TanStack Table逐行row by row过滤的工作方式。使用 match-sorter 系列库是可选的但该库不仅提供模糊过滤还返回每条记录的排名信息ranking从而可以进一步按与查询词的接近程度对结果排序。从源码结构看该库的核心导出在 packages/match-sorter-utils/src/index.ts只包含两个底层工具rankItem对单项计算排名与compareItems比较两个排名并把remove-accents去除变音符号硬编码进包内以简化打包。包的定位正如其源码注释所述不再提供单次调用完成过滤排序的聚合 API而是把排名这一中间产物暴露给宿主系统让过滤与排序可以增量式地组合——这正是 TanStack Table 逐行过滤模型所需要的。排名机制rankings 分数阶梯rankItem的匹配质量由 rankings 常量 定义从高到低依次为分数常量含义7CASE_SENSITIVE_EQUAL大小写敏感地完全相等6EQUAL忽略大小写后完全相等5STARTS_WITH以查询词开头4WORD_STARTS_WITH以查询词作为某个单词开头3CONTAINS包含查询词2ACRONYM匹配首字母缩写如查询ts命中TanStack1MATCHES模糊匹配字符按顺序分散出现在目标串中0NO_MATCH不匹配rankItem返回的RankingInfo包含rank分数、passed是否通过阈值默认阈值即MATCHES、rankedValue被命中的值等字段。getClosenessRanking还会在MATCHES到MATCHES 1之间给出更细粒度的分数——字符在目标串中分布越紧凑、按顺序命中的比例越高分数越接近满分见 源码。这就是模糊排序能把最接近的结果排最前的原理。二、功能装配tableFeatures 与 row models模糊过滤并不是开箱即用的内置功能而是列过滤/全局过滤/行排序三大特性 自定义过滤函数的组合。首先需要把相关 feature 装配进tableFeaturesimport { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, metaHelper, } from tanstack/octane-table import type { RankingInfo } from tanstack/match-sorter-utils interface FuzzyFilterMeta { itemRank?: RankingInfo } const features tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, filteredRowModel: createFilteredRowModel(), // 客户端过滤时启用 // manualFiltering: true, // 服务端过滤手动传入已过滤数据时启用 sortedRowModel: createSortedRowModel(), // 客户端排序时启用 // manualSorting: true, // 服务端排序时启用 filterFns: { fuzzy: fuzzyFilter }, sortFns: { fuzzy: fuzzySort }, filterMeta: metaHelperFuzzyFilterMeta(), }) const table useTable({ features, columns, data, })这里有几个关键点需要说明row model 插槽是类型检查的一部分如果你使用客户端过滤与排序必须在对应 feature 之后装配filteredRowModel: createFilteredRowModel()与sortedRowModel: createSortedRowModel()若改为服务端过滤/排序则用manualFiltering: true/manualSorting: true替换tanstack/octane-table直接重新导出了tanstack/table-core的全部内容见 index.ts所以这些构造函数与核心 API 完全一致。filterMeta插槽metaHelperFuzzyFilterMeta()是类型层面的幻影值phantom value它声明了过滤函数通过addMeta写入行的元数据类型。从源码看metaHelper 在运行时返回空对象仅用于驱动类型推导真正的元数据会落在每行的row.columnFiltersMeta上见 columnFilteringFeature.types.ts 与 createFilteredRowModel.ts。由于它挂在tableFeatures插槽上不再需要declare module做全局类型扩展。按需注册避免打包膨胀上面filterFns/sortFns只登记了本文用到的自定义fuzzy函数。当然你也可以展开内置注册表filterFns: { ...filterFns, fuzzy: fuzzyFilter }但那样会把所有内置过滤/排序函数全部打进 bundle。推荐两种更精简的做法只注册你实际用到的函数或者完全不注册、直接在列定义里传函数本身见下文列过滤与模糊排序两节。三、定义自定义模糊过滤函数模糊过滤的载体是一个自定义FilterFn。它的签名是接收row、columnId与过滤值value通过addMeta回调写入排名元数据最后返回布尔值决定该行是否保留import { rankItem } from tanstack/match-sorter-utils import type { RankingInfo } from tanstack/match-sorter-utils import type { FilterFn, TableFeatures, RowData, metaHelper, } from tanstack/octane-table // 定义模糊过滤写入行的 filter meta 形状 interface FuzzyFilterMeta { itemRank?: RankingInfo } // 扩展 TableFeatures让 FilterFn 类型能访问到 filterMeta 形状 type FuzzyFeatures TableFeatures { filterMeta: FuzzyFilterMeta } const fuzzyFilter: FilterFnFuzzyFeatures, RowData ( row, columnId, value, addMeta, ) { // 用 match-sorter-utils 计算该行在给定列上的排名 const itemRank rankItem(row.getValue(columnId), value) // 把排名信息写入该行的 filter metaaddMeta 是可选的用可选链调用 addMeta?.({ itemRank }) // 返回该行是否通过排名阈值 return itemRank.passed }理解这段代码的三层职责取值row.getValue(columnId)取出该行在columnId列上的原始值交给rankItem与查询值比对。写元数据addMeta({ itemRank })将RankingInfo挂到该行的row.columnFiltersMeta[columnId]。这一步是排序功能的前提——后续的模糊排序需要读取这个排名。addMeta是可选回调因此用addMeta?.()调用。判定保留itemRank.passed即排名分数 ≥ 阈值默认MATCHES。注册时不再使用declare module增强而是通过tableFeatures的插槽登记函数与元数据类型import { metaHelper } from tanstack/octane-table const features tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, filteredRowModel: createFilteredRowModel(), sortedRowModel: createSortedRowModel(), filterFns: { fuzzy: fuzzyFilter }, sortFns: { fuzzy: fuzzySort }, filterMeta: metaHelperFuzzyFilterMeta(), })filterMeta插槽负责给addMeta写入的元数据提供类型filterFns插槽负责按字符串名注册函数之后在列定义的filterFn: fuzzy与表格的globalFilterFn: fuzzy中即可按名引用。四、场景一全局过滤 模糊过滤模糊过滤最常用的场景是全局搜索框用户输入任意关键词表格在所有可过滤列上做模糊匹配。做法与上文相同只需在useTable时把globalFilterFn指定为已注册的fuzzyimport { useTable, tableFeatures, columnFilteringFeature, globalFilteringFeature, rowSortingFeature, createFilteredRowModel, createSortedRowModel, metaHelper, } from tanstack/octane-table const features tableFeatures({ columnFilteringFeature, globalFilteringFeature, rowSortingFeature, filteredRowModel: createFilteredRowModel(), sortedRowModel: createSortedRowModel(), // 若想按模糊排名排序则必须启用 filterFns: { fuzzy: fuzzyFilter }, sortFns: { fuzzy: fuzzySort }, filterMeta: metaHelperFuzzyFilterMeta(), }) const table useTable({ features, columns, data, globalFilterFn: fuzzy, })配套的 UI 通常是一个防抖debounce输入框写入全局过滤状态DebouncedInput value{table.state.globalFilter ?? } onChange{(value) table.setGlobalFilter(String(value))} placeholderSearch all columns... /在官方示例 examples/octane/filters-fuzzy/src/main.tsrx 中全局过滤正是这样接入的globalFilterFn: fuzzy且该示例额外装配了rowPaginationFeature与createPaginatedRowModel()因此在 5 000 行甚至 1 000 000 行示例内置了 Stress Test 按钮的模糊搜索下仍能配合分页稳定运行。示例同时保留了initialState/atoms/state三种状态控制方式的注释说明全局过滤值既可以一次性初始化也可以交给外部 storeatom或受控状态管理。五、场景二列过滤 模糊过滤模糊过滤同样可以限定在单列。注册方式与上面完全一致然后在列定义里用filterFn: fuzzy按名引用const column [ { accessorFn: (row) ${row.firstName} ${row.lastName}, id: fullName, header: Full Name, cell: (info) info.getValue(), filterFn: fuzzy, // 使用我们注册的自定义模糊过滤函数 }, // other columns... ]这里把firstName与lastName拼接成一个fullName列再对拼接结果做模糊过滤——典型场景是姓名全文检索。示例中还展示了混用策略普通列仍然使用内置精确/包含过滤如filterFn: equalsString、includesStringSensitive、includesString只有fullName列启用模糊说明模糊过滤可以与常规过滤函数在同一张表内共存。基于排名的模糊排序fuzzySort当列过滤命中后你往往还希望按与查询词的接近程度对结果排序让最接近的匹配排在最前。这需要自定义SortFn读取过滤阶段写入的排名信息import { compareItems } from tanstack/match-sorter-utils import { sortFn_alphanumeric } from tanstack/octane-table import type { SortFn } from tanstack/octane-table const fuzzySort: SortFnFuzzyFeatures, Person (rowA, rowB, columnId) { let dir 0 // 仅当该列存在排名信息时才按排名比较 if (rowA.columnFiltersMeta[columnId]) { dir compareItems( rowA.columnFiltersMeta[columnId].itemRank!, rowB.columnFiltersMeta[columnId].itemRank!, ) } // 排名相同时回退到字母数字排序 return dir 0 ? sortFn_alphanumeric(rowA, rowB, columnId) : dir }逻辑分两层按排名排序compareItems比较两条记录的RankingInfo分数高者优先见 compareItems 源码。由于排名信息是过滤阶段由addMeta写入row.columnFiltersMeta[columnId]的所以读取前要先判空。排名相同时回退dir 0说明两者排名并列例如都没有过滤命中时此时调用内置的sortFn_alphanumeric源码见 sortFns.ts按字母数字序兜底。使用方式直接把函数传给列定义的sortFn{ accessorFn: row ${row.firstName} ${row.lastName}, id: fullName, header: Full Name, cell: info info.getValue(), filterFn: fuzzy, // 使用我们注册的自定义模糊过滤函数 sortFn: fuzzySort, // 直接传函数跳过注册 }[!NOTE] 注意sortFn: fuzzySort传的是函数本身而filterFn: fuzzy传的是字符串。若想用字符串sortFn: fuzzySort就必须同时把它加进tableFeatures的sortFns插槽如sortFns: { fuzzySort }。直接传函数可完全跳过注册步骤两种方式按需选择。示例 main.tsrx 中则采用了注册方式sortFns: { ..., fuzzy: fuzzySort }且sortFn: fuzzy并配合一个useEffect当columnFilters[0]指向fullName列时自动对fullName列开启排序让模糊过滤结果天然按匹配度排列。六、端到端验证官方为 examples/octane/filters-fuzzy 提供了 Playwright 冒烟测试 smoke.spec.ts启动示例服务后断言表格、表头、首行数据均可见并验证 Regenerate Data 按钮能刷新数据且页面无任何控制台/页面错误。这说明tableFeatures 装配 模糊过滤 分页渲染的组合通过了自动化回归验证你可以把它当作自己接入模糊过滤时的可复现基线。七、小结与注意事项至此你已掌握在tanstack/octane-table中接入模糊过滤的完整链路安装tanstack/match-sorter-utils利用rankItem得到RankingInfopassedrank编写fuzzyFilter排名 →addMeta?.({ itemRank })→ 返回passed通过tableFeatures的filterFns/sortFns/filterMeta插槽注册也可直接在列定义传函数客户端过滤/排序时装配createFilteredRowModel()与createSortedRowModel()服务端则改用manualFiltering/manualSorting全局过滤设置globalFilterFn: fuzzy列过滤设置filterFn: fuzzy需要按匹配度排序时编写fuzzySortcompareItems 字母数字回退。最后提醒几个易错点addMeta可选务必使用addMeta?.()否则在未装配filterMeta插槽的场景下可能访问不存在的回调排序依赖过滤fuzzySort只有在列过滤已运行时才有排名信息可读因此先过滤、再排序是正确的工作流注册范围影响 bundle优先按需注册filterFns/sortFns避免把内置函数全量打入产物类型无需全局扩展Octane 版通过tableFeatures插槽携带filterMeta类型不再需要declare module这类声明合并技巧这也是 v9 架构区别于旧版的重要改进。【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →