当前位置: 首页 > news >正文

羽球联盟 HarmonyOS NEXT 实战系列 (07/20):NavBar、SearchBar、SegmentTab与卡片组件复用

文章导读

  • 常见页面骨架被抽成 NavBar、SearchBar、SegmentTab、Card 等组件。
  • 组件传入数据和回调,不直接耦合业务数据仓库。
  • 卡片组件负责展示一致性,页面负责列表、筛选和跳转。

页面效果

首页、搜索页、赛事页和个人中心会反复出现导航、搜索、分段筛选和卡片。如果每个页面都各写一套顶部结构、筛选条和列表卡片,视觉会逐渐跑偏,交互细节也会开始不一致。羽球联盟把这些高频界面骨架沉到common层后,页面本身只需要关心“当前展示什么数据、点击后去哪里、空态文案怎么写”。

从首页进入搜索、赛事和收藏时,可以明显感觉到这些页面属于同一个产品:标题栏高度一致,搜索输入框的留白和圆角一致,分段筛选的选中反馈一致,卡片的圆角、阴影和底色也一致。组件复用在这里解决的不是“少写几行代码”,而是把跨页面的一致性变成一条可以持续维护的工程约束。

实战拆解

组件复用真正难的地方不是把样式抄出来,而是把边界切对。这个项目里,NavBarSearchBarSegmentTab负责稳定的页面骨架;ArticleCardEquipmentCardPlayerCardEventCard负责不同内容实体的统一展示。它们都遵守同一条规则:组件接收已经整理好的数据和少量回调,不直接知道数据仓库、接口抓取和路由之外的业务细节。

这一点在搜索页最明显。搜索页一边要把本地资讯、联网资讯、本地装备、联网装备合并起来做筛选,一边又要保持上方搜索框和中间分段 Tab 的交互一致。如果把搜索、筛选和卡片渲染全部写死在一个页面里,短期内能跑通,后面要复用就会很痛苦。现在的做法是页面负责组装结果集,再把结果交给通用组件展示,所以搜索页、收藏页、历史页虽然数据来源不同,界面结构却可以保持稳定。

另一个值得注意的点是“通用组件不越层”。例如ArticleCard不去主动读取PrefStore、不自己发起跳转、也不决定收藏状态如何持久化,它只暴露onTap。页面层收到点击后,再决定是记录历史、进入详情,还是加上一层埋点。这样组件的生命周期和职责都更干净,后续迁移到别的列表场景时也不会把旧业务逻辑一起拖过去。

关键代码

build() { Column() { NavBar({ title: '搜索', showBack: true }) Row() { SearchBar({ value: $keyword, placeholder: '搜索资讯、赛事、装备', onSubmit: (t: string): void => { this.onSubmitSearch(t); } }) } .padding({ left: AppSizes.s16, right: AppSizes.s16, top: AppSizes.s8, bottom: AppSizes.s8 }) SegmentTab({ tabs: this.tabs, selectedIndex: $selected }) List({ space: AppSizes.s12 }) { ForEach(this.matchedArticles(), (a: Article) => { ListItem() { ArticleCard({ article: a, onTap: (article: Article): void => { PrefStore.getInstance().addHistory(article.id); Nav.push(Routes.ARTICLE_DETAIL, new RouteParams(article.id)); } }) } }, (a: Article) => 's_a_' + a.id) } } }

这段代码把复用链路展现得很完整。NavBar负责顶栏;SearchBar只维护输入与提交;SegmentTab只同步当前选中的索引;ArticleCard只消费文章模型并把点击回调还给页面。真正和业务强绑定的动作,比如写入浏览历史、进入详情页、选择当前展示哪一种列表,全部留在页面层处理。

对应的卡片组件本身也保持了这个边界。例如ArticleCard会根据coverUrl是否存在决定优先加载远程图还是本地资源兜底,会自己处理标题、标签、阅读数这些视觉细节,但不会擅自去决定点击后发生什么。这种拆法让“同一张卡片出现在首页推荐、搜索结果、收藏页、历史页”变得可行,而且每个页面都还能保留自己的行为差异。

取舍分析

这类复用最容易走向两个极端。一个极端是页面里什么都自己写,短期看最直观,但每多一个页面就要多维护一份顶栏、卡片和筛选条;另一个极端是把组件做得过度聪明,内部直接读取仓库、自己决定路由,最后通用组件反而绑死在某个业务上。羽球联盟选择的是中间路线:把视觉结构和基础交互抽出去,把数据准备和业务决策留在页面里。

这样的取舍会让common层多出一些显式参数,比如SegmentTab需要tabsselectedIndexSearchBar需要@Link valueonSubmit,卡片需要模型对象和点击回调。参数确实多了一点,但好处是组件行为可预测,不会出现“改一个页面,另一个页面跟着被影响”的连锁反应。

另一个收益来自样式统一。AppColorsAppSizes和卡片/顶栏组件一起使用后,圆角、字号、边距、背景色都不需要页面重新发明。后续新增页面时,开发重点就会从“把样式抄得像不像”转移到“这页的数据筛选规则和交互反馈是否正确”,维护成本明显更低。

设计落点

  • 组件只负责展示和回调,不直接读取业务仓库。
  • 页面负责合并本地/远程数据、过滤结果和处理跳转,组件只承接最终状态。
  • NavBarSearchBarSegmentTab先统一页面骨架,再让不同业务页挂接自己的数据流。
  • 卡片组件统一封面、标题、标签、时间和底色等视觉规则,避免列表体验碎片化。
  • 分段筛选、空态和卡片样式统一后,页面新增成本会明显下降。

易踩坑

  • 不要让通用组件直接 import features 数据,否则 common 会变成业务层。
  • 不要把页面私有状态塞进组件内部,选中项、搜索关键字这类状态通常由页面持有更清晰。
  • 不要在卡片组件里顺手写持久化或路由逻辑,否则复用范围一扩大就会反噬。
  • 只抽样式、不抽边界也会出问题。视觉一样但数据模型和回调契约不稳定,组件最后还是难以长期复用。

验证方式

  • 在多个页面观察搜索框、卡片和分段 Tab 是否一致。
  • 空列表时确认 EmptyState 能覆盖不同业务场景。
  • 点击卡片后确认回调交给页面处理,历史记录与页面跳转都正常生效。
  • 在搜索页切换“资讯 / 装备”后确认SegmentTab只切换视图,不污染搜索关键字本身。

参考资料

  • [ArkUI 自定义组件(官方文档)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-create-custom-components-V5)
  • [ArkUI 状态管理 @State / @Link(官方文档)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-state-management-overview-V5)

小结

第 07 篇最核心的结论是:组件复用不是把页面切成更小的块,而是先确定哪些东西应该长期稳定。顶栏、搜索、筛选和卡片属于稳定骨架,就应该沉到common;数据合并、状态切换、点击后的业务动作属于场景差异,就应该留在页面层。这样做以后,新增页面既能保持同一套产品观感,也不会把业务逻辑意外耦合进通用组件。

http://www.jsqmd.com/news/1135064/

相关文章:

  • 工业级条码识别系统:LV30与TM4C129XKCZAD的硬件设计与优化
  • Kimi LeetCode 3485. 删除元素后 K 个字符串的最长公共前缀 Python3实现
  • 2026网文剧本AI工具TOP5比对实测:炼字工坊凭啥力压deepseek?
  • 科研数据可视化新解法:paperxie 一站式 AI 科研绘图,告别 Origin 绘图低效难题
  • 【TwinCAT3运动控制】虚拟轴创建、电子齿轮耦合与寻参功能全解析
  • Kong Audio 3.1.5中文一键安装版 空音3中文版2026最新版中文民乐音色库马头琴音源 Kong Audio Qin Engine 3.1.5中国民乐全套音源下载KongAudio3.15
  • Decoupled and Reusable Adaptation for Efficient Cross-Modal Transfer
  • 【GIS工具】坐标转换工具V2.9发布:支持控制点拟合/残差分析/SHP批量转换
  • PDT集群信令丢包溯源与弱网补偿优化——东北林区广域组网专项优化
  • CTF实战:利用IDNA编码绕过校验与Nginx配置泄露漏洞分析
  • AI 绘图解放科研手作,paperxie 分层制图工具搞定全学科论文可视化
  • Contrastive Clustering (CC) 实战:PyTorch 复现 CIFAR-10 聚类精度 0.705 NMI
  • IMU与MCU硬件协同实现6DoF姿态解算实战
  • PU-Learning 两阶段实战:间谍技术与1-DNF识别可靠负样本的3步代码实现
  • LTC6904与MK64FN1M0VDC12实现高精度方波生成方案
  • Kimi LeetCode 3480. 删除一个冲突对后最大子数组数目 C语言实现
  • Windows 11开始菜单失效:专业级修复方案与ExplorerPatcher深度技术解析
  • BigCLAM 算法 Python 实现:从 AGM 模型到 4 社区划分的 100 步梯度下降
  • 38元/年搭个云服务器:我在阿里云云小站薅到的第一个羊毛(附完整教程)
  • 【第五十二周】结果分析
  • Electron 鸿蒙 PC 上做本地搜索,Fuse.js 比 SQLite 快 6 倍——但我愣是选了最慢的方案
  • Kimi LeetCode 3485. 删除元素后 K 个字符串的最长公共前缀 Java实现
  • SV基础之连接设计和测试平台
  • OpenClaw专栏第三篇:环境搭建前的准备工作清单(保姆级避坑指南)
  • YOLO26 TensorBoard集成:可视化训练过程的完整配置与高级用法
  • Wave-ViT: Unifying Wavelet and Transformersfor Visual Representation Learning ——ECCV
  • 解决UE4SS在Palworld游戏中的崩溃问题:GUI控制台兼容性深度解析
  • 西天取经四人组(Liunx小测试)
  • 在上一篇文章NHibernate实践总结(一)中,主要罗列了自己的一部分实践总结,其中包括:
  • C++ Primer Plus 重读精讲 _ 名称空间与代码组织