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

HarmonyOS知识库——导航架构与路由传参的设计模式

知识库 App 有 6 个页面、4 个底部导航 tab、十几条路由跳转路径。页面之间的跳转不是随意的——每条路由都有明确的参数设计,参数的类型和内容决定了目标页面的行为。搞清楚"从哪来、传什么、怎么用",是理解整个 App 数据流的关键。

完整效果

一、路由参数的完整清单

源页面目标页面参数参数类型
Index(笔记本卡片)NoteListnotebookId, notebookNamenumber, string
Index(底部导航"搜索")NoteListnotebookId=-1, notebookName=‘全部笔记’number, string
Index("+"按钮)NoteEditor
Index(笔记卡片)NoteEditornoteIdnumber
Index(标签点击)TagFilterPagetagstring
Index(底部导航"收藏")FavoritesPage
Index(底部导航"我的")ProfilePage
NoteList(笔记卡片)NoteEditornoteIdnumber
FavoritesPage(笔记卡片)NoteEditornoteIdnumber
ProfilePage(标签云点击)TagFilterPagetagstring
TagFilterPage(标签点击)TagFilterPagetagstring(同页面切换)

11 条路由路径,4 种参数类型:

参数类型出现次数用途
notebookIdnumber2 次按笔记本筛选笔记
notebookNamestring2 次NoteList 标题显示
noteIdnumber3 次编辑指定笔记
tagstring3 次按标签筛选笔记

二、三种参数传递模式

模式一:双参数关联传递

params:{'notebookId':nb.id,'notebookName':nb.name}asRecord<string,Object>

同时传 id 和名称——id 用于查询,名称用于显示。NoteList 收到后用 notebookId 查询笔记列表,用 notebookName 设置导航栏标题。

为什么不只传 id?NoteList 需要显示笔记本名称作为标题。如果只传 id,NoteList 需要自己查 NOTEBOOKS 获取名称——多一次查询。双参数传递让目标页面直接拿到需要的数据。

踩坑记录:最初 Index 跳转 NoteList 只传了 notebookId,NoteList 标题显示为空。查了原因发现没传 notebookName。加了双参数后解决。

模式二:单参数定向传递

// 编辑笔记params:{'noteId':n.id}asRecord<string,Object>// 标签筛选params:{'tag':t.name}asRecord<string,Object>

只传一个参数——目标页面用这个参数做查询或设置状态。NoteEditor 用 noteId 查询笔记详情,TagFilterPage 用 tag 筛选笔记。

为什么不需要传标题或名称?因为目标页面可以从参数查询到完整数据——NoteEditor 通过 noteId 查到笔记的所有字段,TagFilterPage 通过 tag 查到所有匹配笔记。

模式三:特殊值约定

// "搜索" tab 跳转 NoteListparams:{'notebookId':-1,'notebookName':'全部笔记'}asRecord<string,Object>

notebookId=-1 是约定值——表示"不按笔记本筛选,显示全部笔记"。NoteList 收到 -1 后调用getNotes()而不是getNotesByNotebook(-1)

特殊值约定的风险:如果未来 NOTEBOOKS 增加了 id=-1 的笔记本,逻辑就会出错。更安全的做法是不传 notebookId 参数,NoteList 判断参数不存在时显示全部笔记。但在原型阶段,约定值更简单。

三、NoteEditor 的双模式路由

// 新建模式params:{}asRecord<string,Object>// 编辑模式params:{'noteId':n.id}asRecord<string,Object>

有没有 noteId 参数决定了新建还是编辑。NoteEditor 在 aboutToAppear 里判断:

if(p&&p['noteId']){this.note=getNoteById(p['noteId']asnumber)if(this.note){this.isNew=false;/* 预填数据 */}}

有 noteId → 编辑模式 → 预填标题/内容/笔记本/标签
无 noteId → 新建模式 → 所有字段为空

两种模式共享同一个页面——减少代码重复。新建和编辑的 UI 结构一样(标题输入+内容输入+笔记本选择+标签管理),只是数据来源不同。用@State isNew控制差异部分。

新建和编辑的路由参数对比

维度新建编辑
参数空对象{}{ noteId: number }
NoteEditor 判断if (p && p['noteId'])为 false为 true
isNew 值truefalse
导航栏标题“新建笔记”“编辑笔记”
收藏/删除按钮不显示显示
保存操作addNote() 新增修改 note 属性

四、TagFilterPage 的入口差异

// 从首页标签点击params:{'tag':t.name}asRecord<string,Object>// 从 ProfilePage 标签云点击params:{'tag':t.name}asRecord<string,Object>// 直接打开(无参数)无参数

三种入口传相同的参数格式——但初始状态不同。有 tag 参数 → 直接显示筛选结果;无参数 → 显示标签网格供选择。

TagFilterPage 的状态由 selectedTag 控制:

if(p&&p['tag']){this.selectedTag=p['tag']asstringthis.filterNotes()}// selectedTag 有值 → 显示笔记列表// selectedTag 为空 → 显示标签网格

同一个页面两种视图——靠参数决定初始状态。这是"条件渲染"在路由层面的应用。

五、底部导航栏的路由设计

@BuilderNav(label:string,active:boolean){Column({space:2}){Text(label==='首页'?'🏠':(label==='搜索'?'🔍':(label==='收藏'?'❤️':'👤'))).fontSize(18)Text(label).fontSize(9).fontColor(active?A:'#C0BFC6')}.onClick(()=>{if(label==='搜索')router.pushUrl({url:'pages/NoteList',params:{'notebookId':-1,'notebookName':'全部笔记'}asRecord<string,Object>})elseif(label==='收藏')router.pushUrl({url:'pages/FavoritesPage'})elseif(label==='我的')router.pushUrl({url:'pages/ProfilePage'})})}

四个 tab 的路由策略

Tab行为参数原因
首页不跳转已经在首页
搜索跳转 NoteListnotebookId=-1搜索 = 查看全部笔记
收藏跳转 FavoritesPage收藏页独立数据源
我的跳转 ProfilePage个人中心独立页面

“搜索” tab 不是真正的搜索页——它跳转到 NoteList 并传入 notebookId=-1。这是一个设计选择:把"查看全部笔记"和"搜索"合并在同一个页面。NoteList 既有全部笔记列表,又有搜索框,一个页面承担两种功能。

“首页” tab 不跳转的处理

“首页” tab 没有 onClick 跳转逻辑——因为 Index 本身就是首页。如果写了router.pushUrl({ url: 'pages/Index' }),会导致页面重建,搜索框内容丢失。

踩坑记录:最初"首页" tab 也写了跳转,点击后页面闪烁,首页状态丢失。去掉跳转后解决。这是 @Entry 页面的特性——重复 push 同一个 @Entry 会重建实例。

六、返回导航的统一模式

所有非首页页面的返回按钮都是同一段代码:

Row(){SymbolGlyph($r('sys.symbol.chevron_left')).fontSize(20).fontColor([T1])}.width(34).height(34).borderRadius(17).backgroundColor('rgba(0,0,0,0.03)').justifyContent(FlexAlign.Center).onClick(()=>{router.back()})

5 个非首页页面都用了这段代码。结构完全一致——34×34 点击区域、chevron_left 图标、3% 透明度背景、router.back()。

为什么不抽取 Builder?因为这段代码只有 3 行有效逻辑(SymbolGlyph + 样式 + onClick),抽取 Builder 的收益不大。在 5 个页面的规模下,重复 3 行代码比引入 Builder 更简洁。

router.back() 的行为

router.back()返回上一个页面——不需要指定目标。系统自动恢复上一个页面的状态(如果是 @State 页面,状态保留)。

router.pushUrl的区别:

方法行为页面栈
router.pushUrl新页面入栈栈深度 +1
router.back()当前页出栈栈深度 -1

用户操作路径示例:

首页 → 点击笔记 → NoteEditor(栈:首页, NoteEditor) → 点击返回 → 首页(栈:首页)

七、路由参数的类型安全

所有路由参数都用了as Record<string,Object>类型断言:

params:{'noteId':n.id}asRecord<string,Object>

为什么需要类型断言?router.pushUrl 的 params 类型要求Record<string, Object>。直接传{ 'noteId': n.id }会报类型不匹配——因为 TypeScript 推断{ noteId: number }不完全等于Record<string, Object>

接收参数时也需要断言:

constp=router.getParams()asRecord<string,Object>constid:number=p['noteId']asnumber

类型断言是 ArkTS 路由传参的标准做法——虽然繁琐,但保证了类型安全。

八、路由架构的整体模式

页面的角色分类

角色页面特点
入口页Index底部导航栏,不接收路由参数
列表页NoteList, FavoritesPage, TagFilterPage接收参数,渲染列表
详情/编辑页NoteEditor接收 noteId,双模式
展示页ProfilePage不接收参数,纯展示

数据流向

Index(入口) ├→ NoteList(笔记本筛选/搜索) │ └→ NoteEditor(编辑) ├→ NoteEditor(新建) ├→ TagFilterPage(标签筛选) │ └→ TagFilterPage(同页面切换) ├→ FavoritesPage(收藏列表) │ └→ NoteEditor(编辑) └→ ProfilePage(个人中心) └→ TagFilterPage(标签筛选)

所有页面都汇聚到 NoteEditor——它是唯一的"写入"页面。其他页面都是"读取"或"筛选",只有 NoteEditor 能新建和编辑笔记。这种"多入口单出口"的设计让数据写入集中管理,降低了数据不一致的风险。

http://www.cnnetsun.cn/news/3402889.html

相关文章:

  • 5步实现图片到3D打印模型的智能转换
  • 新手必看:yocto-meta-ros项目快速上手攻略
  • 神奇工具:5分钟在Windows上安装安卓应用,告别笨重模拟器
  • C语言学习笔记——动态内存分配
  • 无人机视角航拍河道巡检垃圾及周边坍塌隐患检测数据集VOC+YOLO格式6686张6类别
  • Vue3中computed详解
  • 048、手机影像调优实战:夜景模式、人像虚化与多摄融合的调试经验
  • 技术解析:ChanlunX缠论分析插件在通达信中的实现路径与架构设计
  • CANN/asc-devkit块内同步指令
  • openEuler内核故障排查终极指南:10个常见问题与解决方案大全
  • Loop:5分钟掌握Mac窗口管理终极技巧,告别桌面混乱
  • 从理论到实战:深度解析白盒测试七大覆盖准则及其在代码审查中的应用
  • 如何在Linux系统上轻松安装专业版Photoshop CC 2022:完整指南
  • 从卡文到爆更自由:ChatGPT辅助小说写作全流程拆解,含大纲生成→伏笔回收→风格克隆→版权合规四重关卡
  • CANN Ascend C GetAccVal API文档
  • 收藏!小白程序员轻松入门大模型,6大行业真实案例带你玩转Agent系统!
  • IGBT(绝缘栅双极型晶体管)简介
  • AirSim源码编译
  • 从界面到智能体:前端工程师的Agent开发进阶之路(收藏版)
  • 终极免费教程:5步让老旧Mac完美运行最新macOS系统
  • 技术选型指南:tiny11maker vs tiny11coremaker Windows 11镜像精简技术深度对比与最佳实践
  • Luanti模组开发进阶:从入门到精通的完整教程
  • CANN/asc-devkit SIMD-API数据搬运填充值设置
  • 知识体系即护城河:ChatGPT赋能下的个人IP底层架构(含2024最新RAG+Graph RAG双轨构建协议)
  • Laravel Developer Roadmap:数据库设计与Eloquent ORM实战指南
  • 为什么openeuler/peking_university_2021仓库被关闭?官方说明与社区影响分析
  • 【VScode】无法建立ssh连接问题
  • 遥感、GIS和GPS技术在水文、气象、灾害、生态、环境及卫生等领域中的实践应用
  • CANN/asc-devkit TPipe销毁无管道同步API
  • OpenCode+Copilot:国产化AI编程工作流实战指南