Ant Design Cascader 选项搜索(showSearch):从搜索 Demo 到源码级实现解析
Cascader 是 Ant Design 中用于处理层级数据(如省市区、组织机构、分类树)选择的组件。默认交互是逐级点击菜单完成选择,但当选项层级深、数据量大时,逐级翻找并不高效。本篇文章以仓库中 components/cascader/demo/search.md 的「搜索」示例(对应 search.tsx)为切入点,系统讲解 Cascader[showSearch] 的开启方式、完整配置参数、自定义筛选逻辑与关键词高亮的底层实现,帮助你为省市区、多级菜单等场景快速落地「即输即搜、一键直达」的搜索体验。
搜索 Demo 概览:直接在输入框中搜索并选择
search.md 中对该示例的描述非常精炼:
可以直接搜索选项并选择。(Search and select options directly.)
即:在输入框中直接输入关键词,选项会被实时过滤,高亮命中的部分后直接在结果列表中点选,最终回填为完整的级联路径值。
该示例同时包含一句关键提示:
Cascader[showSearch]暂不支持服务端搜索(Now,Cascader[showSearch]doesn't support search on server)。
也就是说,Cascader 的内置搜索是纯前端的本地过滤,作用于 options 中已加载的数据。当数据量极大、需要远程异步按关键词拉取候选时,不应依赖 showSearch,而应从数据源层面(例如在 lazy.md 展示的动态加载方向)自行设计;官方讨论记录见 ant-design issue #5547。下文的源码分析会进一步印证这一设计边界。
如何开启搜索:Demo 的完整可运行代码
开启方式是把 showSearch 设为 true 或配置对象。为便于理解,这里展开 search.tsx 的核心实现:
import React from 'react';
import { Cascader } from 'antd';
import type { CascaderProps, GetProp } from 'antd';
type DefaultOptionType = GetProp<CascaderProps, 'options'>[number];
interface Option {
value: string;
label: string;
children?: Option[];
disabled?: boolean;
}
const options: Option[] = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{ value: 'xihu', label: 'West Lake' },
{ value: 'xiasha', label: 'Xia Sha', disabled: true },
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [{ value: 'zhonghuamen', label: 'Zhong Hua men' }],
},
],
},
];
const onChange: CascaderProps<Option>['onChange'] = (value, selectedOptions) => {
console.log(value, selectedOptions);
};
// 自定义过滤规则:只要路径上任意一级 label 命中输入值即保留
const filter = (inputValue: string, path: DefaultOptionType[]) =>
path.some((option) =>
(option.label as string).toLowerCase().includes(inputValue.toLowerCase()),
);
const App: React.FC = () => (
<Cascader
options={options}
onChange={onChange}
placeholder="Please select"
showSearch={{ filter, onSearch: (value) => console.log(value) }}
/>
);
export default App;
代码要点:
- 数据是典型的三级结构(
Zhejiang → Hangzhou → West Lake / Xia Sha),其中Xia Sha被标记为disabled,用于演示被禁用的叶子节点不会出现在搜索结果里可被选中的状态(搜索测试中也有对ant-cascader-menu-item-disabled的断言)。 showSearch以对象形式传入,覆盖了filter(过滤规则)与onSearch(监听输入)两个能力。注意onChange的回调参数value是完整路径数组(如['zhejiang', 'hangzhou', 'xihu']),selectedOptions则是命中的完整选项链。- 默认过滤是大小写不敏感的包含匹配,示例中刻意对
inputValue与label都执行toLowerCase()再判断。
showSearch 完整配置项详解
根据 Cascader API 文档(英文版见 index.en-US.md),showSearch 可接受 boolean 或如下配置对象。当传入 true 时相当于启用一套内置默认行为(详见下文「合并逻辑」)。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
autoClearSearchValue |
选中项后是否清空搜索框,仅在 multiple 为 true 时有效 |
boolean |
true |
5.9.0 |
filter |
接收 (inputValue, path),当 path 符合筛选条件时返回 true 保留,否则排除 |
(inputValue, path) => boolean |
- | - |
limit |
搜索结果展示数量上限 | number | false |
50 |
- |
matchInputWidth |
搜索结果列表是否与输入框同宽 | boolean |
true |
- |
render |
用于渲染过滤后的选项 | (inputValue, path) => ReactNode |
- | - |
sort |
对过滤后的选项排序 | (a, b, inputValue) |
- | - |
searchValue |
受控的搜索值,需与 showSearch 配合 |
string |
- | 4.17.0 |
onSearch |
监听搜索输入,回调返回输入的值 | (search: string) => void |
- | 4.17.0 |
searchIcon |
自定义搜索图标 | ReactNode |
- | 6.3.0 |
在组件顶层(而非 showSearch 对象内)也出现过 searchValue 与 onSearch 两个属性,它们在文档中已被标记为 deprecated(删除线),官方建议改用上述 showSearch 对象内的同名字段。
几个值得注意的默认值与行为:
limit默认 50:当命中结果很多时仅展示前 50 条,可通过limit: false关闭数量上限,或调大数值。仓库测试中通过limit: 1断言结果仅保留 1 条,验证了该字段生效路径(index.test.tsx)。filter语义是按「路径」过滤:传入的第二个参数是从根到当前节点的整条path,因此可以实现「只要路径上任意一级匹配就展示该分支」的级联搜索,这正是 Demo 中path.some(...)写法存在的意义——例如输入West也能定位到Zhejiang / Hangzhou / West Lake。- 无命中结果:会展示
notFoundContent(默认「暂无数据」,即 Empty 空状态)。测试覆盖了自定义文案与notFoundContent={null}隐藏空态两种场景(index.test.tsx)。
源码侧看实现:搜索如何与 Cascader 组装
showSearch 的合并逻辑
在 components/cascader/index.tsx 中,组件会对 showSearch 做一次归一化处理:
const mergedShowSearch = React.useMemo(() => {
if (!showSearch) {
return showSearch;
}
let searchConfig: SearchConfig = {
render: defaultSearchRender,
};
if (isPlainObject(showSearch)) {
searchConfig = { ...searchConfig, ...showSearch };
}
return searchConfig;
}, [showSearch]);
这段代码说明三件事:
- 未开启
showSearch时原样透传(不产生任何搜索能力); - 只要开启,Ant Design 就会注入一个默认的
render实现defaultSearchRender,保证即使你只写showSearch,结果渲染也带有关键词高亮; - 传入对象时通过浅合并覆盖默认项,因此
filter、render、limit、sort、searchValue、onSearch、searchIcon都是可定制的。
合并后的配置最终透传给底层实现 @rc-component/cascader 的 RcCascader 组件(见 index.tsx),即搜索过滤、输入监听等核心机制复用 rc 生态的 Select 系实现。
默认结果渲染与关键词高亮
默认 render 为 defaultSearchRender(index.tsx),它负责把命中的完整路径渲染为 标签 / 父级 / 命中项 的分段形式,并且调用 highlightKeyword 对命中片段做高亮包装。
highlightKeyword(index.tsx)的实现思路是把原文按关键词小写拆分后重组,命中片段用带类名 ${prefixCls}-menu-item-keyword 的 <span> 包裹,从而在搜索结果中呈现出黄色关键词高亮效果。搜索相关快照测试(如 should highlight keyword and filter when search in Cascader)正是围绕该高亮结果展开的(index.test.tsx)。
搜索图标
搜索模式下输入框左侧会展示放大镜图标,可通过 showSearch.searchIcon 或 ConfigProvider 全局配置(searchIcon token 于 6.4.0 支持组件级配置)自定义。在源码中,当 showSearch 为对象时,其 searchIcon 会被提取并并入 Select 图标逻辑:searchIcon: isPlainObject(showSearch) ? showSearch.searchIcon : undefined(index.tsx)。
与多选、动态加载的配合边界
源码注释与 API 文档给出两条重要约束,与 search.md 的「不支持服务端搜索」提示互为补充:
loadData(动态加载)无法与showSearch同时使用(见 index.zh-CN.md API 表),原因在于远程数据在键入时尚未全部就绪,本地过滤无从谈起;- 搜索输入对单选、多选(
multiple)场景都生效,但autoClearSearchValue只在multiple为true时有意义——单选选中后输入框自然关闭,多选场景才需要决定是否保留当前搜索词以便继续追加选择。
搜索行为的可验证细节
仓库测试为搜索行为提供了多组可验证的断言(components/cascader/tests/index.test.tsx),可据此确认实际交互:
- 键入即触发过滤与展开:向输入框输入内容后浮层展开并展示过滤结果;
- 退格清空关键词后关闭浮层:
backspace清空后下拉自动收起; - 点击清除按钮会同步清空搜索词:输入搜索词后点击清除,输入框 value 归空;
- options 变化时结果同步更新:rerender 新的
options后,过滤出的菜单项数量随之变化; - 方向键下 + 回车可直接选中:搜索命中后可直接用键盘选中,无需回到层级菜单;
- 禁用项正确显示禁用态:命中的禁用节点带有
-disabled类名,不可选中。
这些测试与 demo 一同证明:开启 showSearch 后,Cascader 的体验向普通 Select 的搜索对齐——输入、过滤、高亮、键盘操作与空态提示都已内置。Demo 本身也通过 demo.test.tsx(components/cascader/tests/demo.test.tsx)在测试套件中持续运行,保证示例代码可维护、可运行。
小结与实践建议
围绕 components/cascader/demo/search.md 所对应的「搜索」Demo,可以得出如下可直接落地的结论:
- 小到中型、已完整加载的级联数据:直接
showSearch,配合自定义filter(按path路径匹配)即可获得带高亮的级联搜索; - 命中结果可能很多:用
limit控制展示条数,避免性能与视觉负担; - 需要定制结果文案或排序:通过
render与sort实现,默认值已包含关键词高亮; - 多选场景:合理利用
autoClearSearchValue决定选中后是否保留关键词; - 海量数据或远程搜索诉求:
showSearch是纯前端过滤,不支持服务端搜索,且与loadData互斥——应转向自行实现远程候选源 + 自定义下拉内容(popupRender/optionRender)的路线。
把握住「本地路径过滤 + 关键词高亮 + 前端数据源」这一定位,Cascader 的搜索能力即可在省市区选择、组织树导航、多级类目筛选等典型场景中发挥出最大价值。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0625
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00