鸿蒙新特性——Search 搜索组件详解

鸿蒙新特性——Search 搜索组件详解
一、引言搜索是移动端应用最基础的功能入口之一。从 App Store 的搜索标签到微信通讯录的找人功能从电商 App 的商品搜索到知识库的文章检索——搜索框是用户与海量内容之间的桥梁。在 HarmonyOS NEXT 之前开发者若要实现一个搜索功能需要自行组合 TextInput Image Button手动管理焦点状态和搜索逻辑——代码分散且交互细节难以统一。ArkUI 在 API 10 中新增了Search组件将搜索框所有元素封装为一个整体自带搜索图标、占位提示、搜索按钮和自动焦点管理。开发者只需声明初始值和占位文字再配合onChange实时输入和onSubmit提交搜索两个事件就能获得完整的搜索交互体验。本文通过一个本地知识库搜索Demo 深入讲解 Search 组件的核心用法如何区分onChange实时过滤和onSubmit确认搜索如何实现搜索历史记录如何处理空结果状态以及如何将 Search 组件融入完整的搜索业务流程。阅读完本文你将能够使用 Search 组件替代手动的搜索栏布局区分onChange实时过滤和onSubmit确认搜索两种事件模式实现搜索历史的记录、去重和数量管理处理搜索过程中的三种视图状态初始、有结果、无结果将 Search 组件与 ForEach 列表过滤组合为完整的搜索页二、Search 组件 API 总览2.1 构造函数参数Search({placeholder:搜索文章...,value:this.query})参数类型说明placeholderstring占位提示文字。当搜索框为空时显示引导用户输入valuestring搜索框的当前文本值。与State绑定可实现双向数据流iconstring搜索图标。默认为系统搜索图标可传入 Symbol Glyph 名称自定义placeholder是用户体验的关键——好的占位文字不仅提示这里能搜索还提示能搜什么。例如搜索文章标题、内容…明确告知搜索范围比请输入关键词更有信息量。value是 Search 组件的核心绑定值传入State query后用户每次输入都会更新query同时外部修改query如点击搜索历史标签也会同步反映到搜索框中。这是典型的双向数据流模式。2.2 属性方法searchButton(value: string)设置搜索按钮的文字默认显示搜索Search({placeholder:搜索...,value:this.query}).searchButton(搜索)// 搜索按钮文案//.searchButton(Go) // 也可以使用简短文案//.searchButton() // 空字符串隐藏搜索按钮搜索按钮显示在搜索框右侧点击后触发onSubmit事件。如果不希望显示搜索按钮纯实时过滤场景可以传入空字符串。但保留搜索按钮能给用户一个明确的执行搜索的操作目标推荐保留。width / height控制搜索框尺寸Search({placeholder:搜索...,value:this.query}).width(100%)// 占满父容器宽度.height(40)// 推荐 36-44vp在移动端应用中搜索框通常占满屏幕宽度。高度推荐 36-44vp——足够容纳手指点击但不过度占用垂直空间。backgroundColor / borderRadius控制搜索框背景色和圆角.backgroundColor(#F2F3F5)// 浅灰背景.borderRadius(8)// 圆角2.3 事件回调onChange(callback: (value: string) void)搜索框内容变化时触发。用户每次输入/删除一个字符都会触发Search({placeholder:搜索...,value:this.query}).onChange((value:string){this.queryvalue;// 实时过滤逻辑...})onChange是实时过滤filter-as-you-type的核心事件。在本地搜索场景中数据已在本地每次输入后立即过滤列表让用户无需点击搜索按钮就能看到结果更新。这种体验比输入 → 点搜索 → 看结果少一步操作在本地搜索中推荐使用。onSubmit(callback: (value: string) void)用户按下键盘回车键或点击搜索按钮时触发Search({placeholder:搜索...,value:this.query}).onSubmit((value:string){// 提交搜索 → 保存历史 远程查询})onSubmit用于需要确认的场景——远程搜索网络请求、保存搜索历史、触发搜索埋点等。即使主搜索流程使用onChange实时过滤onSubmit也可以用来保存搜索历史两者不矛盾。2.4 onChange 与 onSubmit 的选择场景推荐事件理由本地数据过滤onChange即时反馈无需额外点击远程 API 搜索onSubmit避免每次输入触发网络请求搜索历史记录onSubmit只有确认的搜索才值得记录过滤 历史都做都用onChange 过滤 onSubmit 保存历史本地搜索是最常见的 Search 使用场景——文章列表、通讯录、设置项等。数据量通常在几百到几千条之间客户端过滤完全可行。远程搜索用于数据量极大或数据不在本地的场景如电商商品搜索、地图 POI 搜索。三、Demo 设计本地知识库搜索3.1 功能概述Demo 模拟一个本地知识库搜索页面内置 10 篇 ArkUI 技术文章。用户输入关键词后实时过滤文章列表匹配标题、摘要和分类。搜索历史记录在本地状态中最多保留 8 条支持点击历史标签快速搜索和一键清除。10 篇文章覆盖 ArkUI 开发的常见主题文章分类关键词ArkUI声明式UI范式详解ArkUI基础声明式、容器、布局State与Prop状态管理实战状态管理State、Prop、装饰器页面路由与导航设计导航路由router、pushUrl、导航动画曲线与交互反馈动画特效animateTo、Curve、动画HTTP请求与数据缓存策略网络请求HTTP、缓存、请求List与Grid高性能渲染列表渲染List、Grid、LazyForEach自定义组件与Builder复用组件化Builder、自定义组件手势识别与事件处理机制交互事件手势、事件、Pan主题定制与样式系统样式主题Theme、深色模式、样式性能优化与调试技巧精讲性能优化性能、优化、SmartPerf3.2 数据结构interfaceArticleItem{title:string;// 标题category:string;// 分类summary:string;// 摘要date:string;// 日期}Statequery:string;StatefilteredArticles:ArticleItem[][];StatesearchHistory:string[][];StatehasSearched:booleanfalse;query搜索框绑定值双向数据流filteredArticles过滤后的文章列表初始 全部文章searchHistory搜索历史数组通过不可变更新维护hasSearched是否执行过搜索——用于区分初始全部展示和搜索无结果状态3.3 实时过滤逻辑搜索的核心逻辑在doSearch方法中doSearch(q:string):void{constlowerq.toLowerCase();constresult:ArticleItem[][];for(leti0;ithis.allArticles.length;i){constathis.allArticles[i];if(a.title.toLowerCase().indexOf(lower)!-1||a.summary.toLowerCase().indexOf(lower)!-1||a.category.toLowerCase().indexOf(lower)!-1){result.push(a);}}this.filteredArticlesresult;this.resultCountresult.length;}搜索范围覆盖三个字段标题、摘要和分类。使用indexOf做子串匹配而非精确匹配确保路由能匹配到导航路由。搜索忽略大小写toLowerCase提升容错性。onChange和onSubmit都调用doSearch但onSubmit额外记录搜索历史onSearchChange(value:string):void{this.queryvalue;constqvalue.trim();if(q){this.filteredArticlesthis.allArticles;this.hasSearchedfalse;return;}this.hasSearchedtrue;this.doSearch(q);}onSearchSubmit(value:string):void{constqvalue.trim();if(q)return;this.hasSearchedtrue;this.addToHistory(q);// 仅 onSubmit 记录历史this.doSearch(q);}注意onChange中当搜索框清空时恢复到全部文章列表——这是重要的用户体验细节。用户删除所有文字后应该看到完整的文章列表而不是停留在上一次的搜索结果。3.4 搜索历史管理搜索历史使用不可变数组更新模式addToHistory(q:string):void{constnewHistory:string[][];// 去重遍历旧历史跳过相同词for(leti0;ithis.searchHistory.length;i){if(this.searchHistory[i]!q){newHistory.push(this.searchHistory[i]);}}// 新词插到最前面newHistory.unshift(q);// 最多保留 8 条if(newHistory.length8){newHistory.pop();}this.searchHistorynewHistory;}这个逻辑实现了三个功能去重如果搜索词已在历史中先移除旧的再添加新的移到最前面排序最近搜索的词在最前面unshift插入到数组头部上限最多保留 8 条pop移除最早的历史为什么要限制 8 条因为搜索历史用 Flex Wrap 标签展示8 个标签约 2-3 行——如果无限制历史标签会占据过多空间挤压搜索结果区域。点击历史标签复用搜索词tapHistory(q:string):void{this.queryq;// 填入搜索框双向绑定更新 UIthis.hasSearchedtrue;this.doSearch(q);// 立即执行搜索}清除所有历史clearHistory():void{this.searchHistory[];}点击清除后历史区域通过条件渲染if (this.searchHistory.length 0)直接消失。3.5 三种视图状态搜索页面有三个视图状态通过条件渲染管理状态 1初始状态未搜索Search 搜索框显示搜索历史区域如果有历史记录全部文章列表10 篇hasSearched false query 状态 2有结果状态Search 搜索框显示含输入文字搜索历史消失因为正在搜索中结果计数“找到 X 条结果”过滤后的文章列表hasSearched true filteredArticles.length 0状态 3无结果状态Search 搜索框显示含输入文字搜索历史消失结果计数“找到 0 条结果”空状态提示 “没有找到相关文章”hasSearched true filteredArticles.length 0三态切换通过条件渲染自然实现不需要额外的状态机。关键是用hasSearched区分还没搜过和搜过了但是没结果——如果只靠filteredArticles.length 0判断初始状态还没搜也会被误判为无结果状态。3.6 文章详情弹窗点击文章卡片展示详情弹窗包含分类标签、标题、日期和完整摘要showArticleDetail(article:ArticleItem):void{this.selectedArticlearticle;this.showDetailtrue;}弹窗使用Stack自建而非系统 AlertDialog——可以展示任意布局分类标签 标题 日期 正文。半透明背景层点击可关闭弹窗。3.7 页面结构┌──────────────────────────────────────────┐ │ 本地搜索深色标题栏 │ ├──────────────────────────────────────────┤ │ Search 组件说明卡片 │ ├──────────────────────────────────────────┤ │ 搜索文章标题、内容... [搜索] │ ← Search 组件 ├──────────────────────────────────────────┤ │ ┌────────────────────────────────────┐ │ │ │ 搜索历史 清除 │ │ │ │ [路由] [State] [状态] [动画] │ │ ← 仅 query 为空时显示 │ └────────────────────────────────────┘ │ ├──────────────────────────────────────────┤ │ 找到 3 条结果 │ ├──────────────────────────────────────────┤ │ ┌────────────────────────────────────┐ │ │ │ [导航路由] 2026-04-25 │ │ │ │ 页面路由与导航设计 │ │ │ │ 使用router.pushUrl实现页面跳转... │ │ │ └────────────────────────────────────┘ │ │ ┌────────────────────────────────────┐ │ │ │ [导航路由] 2026-04-25 │ │ │ │ ...更多文章卡片... │ │ │ └────────────────────────────────────┘ │ └──────────────────────────────────────────┘四、Search 组件的最佳实践4.1 搜索范围的选择搜索范围搜索哪些字段直接影响搜索的准确性和用户体验范围太窄用户用布局搜不到ArkUI声明式UI范式详解虽然内容涉及布局但标题没提到→ 用户觉得搜不到东西范围太广搜索的匹配到几乎所有文章 → 搜索结果失去意义推荐搜索范围标题 内容摘要 分类标签。标题是核心匹配项权重最高摘要提供补充匹配覆盖更多关键词分类标签匹配用于按类别搜索的场景如搜索动画找到动画类文章。4.2 onChange vs onSubmit 的实战选择在实际项目中选择 onChange 还是 onSubmit 取决于搜索成本本地数据1000 条使用onChange。遍历过滤开销极小毫秒级用户享受即时反馈。本地数据1000 条仍可使用onChange但建议做防抖debounce——用户停止输入 300ms 后再执行过滤避免每次按键都遍历大量数据。远程搜索使用onSubmit。每次搜索都是一次网络请求必须由用户主动触发。本 Demo 使用onChange作为主搜索事件10 篇文章遍历耗时 1msonSubmit用来保存搜索历史。这种onChange 过滤 onSubmit 记录的模式是本地搜索的最佳实践。4.3 搜索历史的用户体验搜索历史的几个设计细节去重 前移如果用户再次搜索同一个词旧的记录应该移除新的记录移到最前面——因为最近搜索比首次搜索更有参考价值。数量上限推荐 5-10 条。太少——用户可能找不到几天前的搜索太多——占据过多屏幕空间。仅记录有效搜索只在onSubmit中记录历史不在onChange中记录——用户输入到一半时删除文字不应产生历史记录。一键清除清除按钮应放在历史区域右上角使用较为明显的颜色如红/橙色——这不是常用操作但要容易找到。4.4 空状态设计空结果页面不是报错而是引导不要显示红色的错误信息——搜索无结果不是错误使用轻松的语气和图标“ 没有找到相关文章试试其他关键词”给出建议“尝试搜索路由、状态、动画”可选——列出几个热门关键词引导用户一个好的空状态不仅告知没有结果还帮助用户理解原因“这个关键词不匹配vs没有任何文章”。4.5 搜索与键盘的配合Search 组件在 HarmonyOS 中自动处理焦点和键盘弹出。需要关注的细节首次进入页面Search 不应自动获取焦点——用户可能要浏览历史或文章列表自动弹出键盘会遮挡内容。点击搜索按钮后键盘自动收起Search 组件默认行为。滚动列表时键盘不应自动收起——用户可能在键盘可见时浏览结果。如果需要在特定时机手动获取焦点可以使用focusable和focusOnTouch属性控制。五、完整代码结构SearchPage (~220 行) ├── 数据定义 │ ├── ArticleItem 接口 — title / category / summary / date │ └── allArticles[10] — 10 篇内置文章 ├── 状态变量 │ ├── State query — 搜索框文本 │ ├── State filteredArticles — 过滤结果 │ ├── State searchHistory — 搜索历史最多 8 条 │ ├── State hasSearched — 是否已搜索 │ └── State showDetail / selectedArticle — 详情弹窗 ├── 业务逻辑 │ ├── doSearch() — 标题摘要分类匹配 │ ├── onSearchChange() — onChange 实时过滤 │ ├── onSearchSubmit() — onSubmit 过滤记录历史 │ ├── addToHistory() — 去重前移限数量 │ ├── tapHistory() — 点击历史标签搜索 │ └── clearHistory() — 清空历史 ├── 视图 │ ├── 标题栏 — 本地搜索 │ ├── 说明卡片 — Search 组件介绍 │ ├── Search 组件 — 占位文字 搜索按钮 onChange onSubmit │ ├── 搜索历史区域Flex Wrap 标签 清除按钮 │ ├── 结果计数(找到 X 条结果) ForEach 文章列表 │ └── 空状态 没有找到相关文章 └── 详情弹窗Stack 叠加六、总结本文通过一个本地知识库搜索Demo 深入讲解了 HarmonyOS NEXT 中的Search 搜索组件。Search 将传统的手动搜索栏布局封装为声明式组件通过onChange实时过滤和onSubmit确认搜索两个事件覆盖本地和远程两种搜索场景。核心要点回顾Search 的双事件模式onChange用于实时本地过滤filter-as-you-typeonSubmit用于确认搜索和保存历史。两者可以共存——onChange 负责过滤onSubmit 负责记录。搜索范围的权衡标题 摘要 分类是最实用的搜索范围组合。标题提供精准匹配摘要扩展覆盖分类支持按类别浏览。搜索历史的三个操作去重避免重复记录、前移最近搜索在前、限数量最多 8 条。这些操作通过不可变数组模式实现。三种视图状态的切换初始态全部文章 搜索历史、有结果态过滤列表 计数、无结果态空状态提示。hasSearched状态变量区分还没搜和搜了没结果。Search 是受控组件通过value属性与State绑定实现双向数据流——用户输入自动更新状态外部修改状态如点击历史标签同步更新搜索框。Search 组件是 HarmonyOS NEXT 中实用优先的典型代表——它的 API 只有 3 个参数 2 个事件但覆盖了搜索交互的核心需求。不需要手动拼装 TextInput Image Button不需要管理焦点和键盘逻辑——Search 让搜索功能回归到声明事件 处理数据这一本质。七、扩展思考Search 组件解决的是搜索框的 UI 和交互问题但完整的搜索体验还包括以下方面搜索结果高亮在结果列表中高亮显示匹配的关键词——例如搜索路由标题中的路由二字用蓝色标记。这需要 Text 组件的 Span 子组件实现分片渲染将匹配文本与非匹配文本使用不同颜色。搜索建议在搜索框中输入时下方展示搜索建议自动补全。这需要额外的建议词列表和下拉 UI。搜索排序当前过滤结果保持原始顺序。在实际项目中可能需要按相关度排序——标题匹配权重 摘要匹配 分类匹配甚至支持拼音搜索和模糊匹配。远程搜索当数据源是远程 API 时需要管理加载状态搜索中显示加载动画、错误状态网络错误提示和防抖用户停止输入 500ms 后再发请求。这些扩展说明Search 组件是搜索体验的起点它解决了搜索框本身的问题而搜索的深度和精度取决于搜索逻辑层的设计。理解 Search 的核心机制双向绑定、双事件模式、视图状态切换是构建任何搜索功能的基础。