news 2026/7/15 15:14:30

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 能新建和编辑笔记。这种"多入口单出口"的设计让数据写入集中管理,降低了数据不一致的风险。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/15 15:13:23

5步实现图片到3D打印模型的智能转换

5步实现图片到3D打印模型的智能转换 【免费下载链接】ImageToSTL This tool allows you to easily convert any image into a 3D print-ready STL model. The surface of the model will display the image when illuminated from the left side. 项目地址: https://gitcode.…

作者头像 李华
网站建设 2026/7/15 15:13:08

新手必看:yocto-meta-ros项目快速上手攻略

新手必看&#xff1a;yocto-meta-ros项目快速上手攻略 【免费下载链接】yocto-meta-ros ROS (Robot Operating System) support layer 项目地址: https://gitcode.com/openeuler/yocto-meta-ros 前往项目官网免费下载&#xff1a;https://ar.openeuler.org/ar/ 你是否在…

作者头像 李华
网站建设 2026/7/15 15:12:59

神奇工具:5分钟在Windows上安装安卓应用,告别笨重模拟器

神奇工具&#xff1a;5分钟在Windows上安装安卓应用&#xff0c;告别笨重模拟器 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾想在Windows电脑上运行手机应用…

作者头像 李华
网站建设 2026/7/15 15:12:45

C语言学习笔记——动态内存分配

目录1 动态内存分配常用库函数1.1 free1.2 malloc1.3 calloc1.4 realloc2 常见的动态内存错误2.1 对NULL指针的使用2.2 越界访问2.3 对非动态开辟的空间进行释放2.4 对动态开辟的空间重复释放2.5 释放部分动态开辟的空间2.6 忘记释放动态开辟的内存空间3 柔性数组1 动态内存分配…

作者头像 李华
网站建设 2026/7/15 15:11:03

Vue3中computed详解

Vue3中computed详解1. 核心特性2. 基本用法2.1 只读计算属性&#xff08;最常用&#xff09;2.2 可写计算属性3. computed vs methods4. 进阶原理与优化 (Vue 3.4)5. 最佳实践与注意事项6.总结在 Vue 3 中&#xff0c;computed&#xff08;计算属性&#xff09;是处理响应式数据…

作者头像 李华