
知识库 App 有 6 个页面、4 个底部导航 tab、十几条路由跳转路径。页面之间的跳转不是随意的——每条路由都有明确的参数设计参数的类型和内容决定了目标页面的行为。搞清楚从哪来、传什么、怎么用是理解整个 App 数据流的关键。完整效果一、路由参数的完整清单源页面目标页面参数参数类型Index笔记本卡片NoteListnotebookId, notebookNamenumber, stringIndex底部导航搜索NoteListnotebookId-1, notebookName‘全部笔记’number, stringIndex按钮NoteEditor无—Index笔记卡片NoteEditornoteIdnumberIndex标签点击TagFilterPagetagstringIndex底部导航收藏FavoritesPage无—Index底部导航我的ProfilePage无—NoteList笔记卡片NoteEditornoteIdnumberFavoritesPage笔记卡片NoteEditornoteIdnumberProfilePage标签云点击TagFilterPagetagstringTagFilterPage标签点击TagFilterPagetagstring同页面切换11 条路由路径4 种参数类型参数类型出现次数用途notebookIdnumber2 次按笔记本筛选笔记notebookNamestring2 次NoteList 标题显示noteIdnumber3 次编辑指定笔记tagstring3 次按标签筛选笔记二、三种参数传递模式模式一双参数关联传递params:{notebookId:nb.id,notebookName:nb.name}asRecordstring,Object同时传 id 和名称——id 用于查询名称用于显示。NoteList 收到后用 notebookId 查询笔记列表用 notebookName 设置导航栏标题。为什么不只传 idNoteList 需要显示笔记本名称作为标题。如果只传 idNoteList 需要自己查 NOTEBOOKS 获取名称——多一次查询。双参数传递让目标页面直接拿到需要的数据。踩坑记录最初 Index 跳转 NoteList 只传了 notebookIdNoteList 标题显示为空。查了原因发现没传 notebookName。加了双参数后解决。模式二单参数定向传递// 编辑笔记params:{noteId:n.id}asRecordstring,Object// 标签筛选params:{tag:t.name}asRecordstring,Object只传一个参数——目标页面用这个参数做查询或设置状态。NoteEditor 用 noteId 查询笔记详情TagFilterPage 用 tag 筛选笔记。为什么不需要传标题或名称因为目标页面可以从参数查询到完整数据——NoteEditor 通过 noteId 查到笔记的所有字段TagFilterPage 通过 tag 查到所有匹配笔记。模式三特殊值约定// 搜索 tab 跳转 NoteListparams:{notebookId:-1,notebookName:全部笔记}asRecordstring,ObjectnotebookId-1 是约定值——表示不按笔记本筛选显示全部笔记。NoteList 收到 -1 后调用getNotes()而不是getNotesByNotebook(-1)。特殊值约定的风险如果未来 NOTEBOOKS 增加了 id-1 的笔记本逻辑就会出错。更安全的做法是不传 notebookId 参数NoteList 判断参数不存在时显示全部笔记。但在原型阶段约定值更简单。三、NoteEditor 的双模式路由// 新建模式params:{}asRecordstring,Object// 编辑模式params:{noteId:n.id}asRecordstring,Object有没有 noteId 参数决定了新建还是编辑。NoteEditor 在 aboutToAppear 里判断if(pp[noteId]){this.notegetNoteById(p[noteId]asnumber)if(this.note){this.isNewfalse;/* 预填数据 */}}有 noteId → 编辑模式 → 预填标题/内容/笔记本/标签无 noteId → 新建模式 → 所有字段为空两种模式共享同一个页面——减少代码重复。新建和编辑的 UI 结构一样标题输入内容输入笔记本选择标签管理只是数据来源不同。用State isNew控制差异部分。新建和编辑的路由参数对比维度新建编辑参数空对象{}{ noteId: number }NoteEditor 判断if (p p[noteId])为 false为 trueisNew 值truefalse导航栏标题“新建笔记”“编辑笔记”收藏/删除按钮不显示显示保存操作addNote() 新增修改 note 属性四、TagFilterPage 的入口差异// 从首页标签点击params:{tag:t.name}asRecordstring,Object// 从 ProfilePage 标签云点击params:{tag:t.name}asRecordstring,Object// 直接打开无参数无参数三种入口传相同的参数格式——但初始状态不同。有 tag 参数 → 直接显示筛选结果无参数 → 显示标签网格供选择。TagFilterPage 的状态由 selectedTag 控制if(pp[tag]){this.selectedTagp[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:全部笔记}asRecordstring,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新页面入栈栈深度 1router.back()当前页出栈栈深度 -1用户操作路径示例首页 → 点击笔记 → NoteEditor栈首页, NoteEditor → 点击返回 → 首页栈首页七、路由参数的类型安全所有路由参数都用了as Recordstring,Object类型断言params:{noteId:n.id}asRecordstring,Object为什么需要类型断言router.pushUrl 的 params 类型要求Recordstring, Object。直接传{ noteId: n.id }会报类型不匹配——因为 TypeScript 推断{ noteId: number }不完全等于Recordstring, Object。接收参数时也需要断言constprouter.getParams()asRecordstring,Objectconstid:numberp[noteId]asnumber类型断言是 ArkTS 路由传参的标准做法——虽然繁琐但保证了类型安全。八、路由架构的整体模式页面的角色分类角色页面特点入口页Index底部导航栏不接收路由参数列表页NoteList, FavoritesPage, TagFilterPage接收参数渲染列表详情/编辑页NoteEditor接收 noteId双模式展示页ProfilePage不接收参数纯展示数据流向Index入口 ├→ NoteList笔记本筛选/搜索 │ └→ NoteEditor编辑 ├→ NoteEditor新建 ├→ TagFilterPage标签筛选 │ └→ TagFilterPage同页面切换 ├→ FavoritesPage收藏列表 │ └→ NoteEditor编辑 └→ ProfilePage个人中心 └→ TagFilterPage标签筛选所有页面都汇聚到 NoteEditor——它是唯一的写入页面。其他页面都是读取或筛选只有 NoteEditor 能新建和编辑笔记。这种多入口单出口的设计让数据写入集中管理降低了数据不一致的风险。