前言
Navigation是 HarmonyOS ArkUI 6.x 推荐的页面导航框架,取代传统的router.push/pop模式,实现组件内路由管理。它内置 AppBar(顶部导航栏)、ToolBar(底部工具栏)、侧边导航等能力,并通过NavPathStack管理路由栈。本文完整演示从列表页到详情页的路由跳转实现。
运行效果
初始状态(列表页)
点击条目进入详情页
核心 API 一览
| API | 说明 |
|---|---|
Navigation(pathStack) | 导航根容器,绑定路由栈 |
NavPathStack | 路由栈管理器,控制页面的 push/pop |
NavDestination | 路由目标页面容器,类似 Destination |
.navDestination(builder) | 注册路由表:名称 → 组件的映射 |
.title(string/CustomBuilder) | AppBar 标题,支持自定义 |
.mode(NavigationMode) | 导航模式:Stack / Split / Auto |
.toolbarConfiguration([]) | 底部工具栏配置 |
pathStack.pushPathByName(name, param) | 按名称跳转 |
pathStack.pop() | 返回上一页 |
pathStack.popToRoot() | 返回根页 |
完整示例代码
interface ArticleItem { id: number title: string summary: string content: string author: string time: string icon: string tag: string readTime: string } // 路由参数结构 interface ArticleParam { articleId: number } @Entry @Component struct Index { private pathStack: NavPathStack = new NavPathStack() private articles: ArticleItem[] = [ { id: 1, icon: '📐', tag: '布局', title: 'Stack 层叠布局与 Overlay 浮层详解', summary: '深入理解 alignContent 九宫格、zIndex 层叠顺序与 overlay 浮层实战。', content: 'Stack 是 ArkUI 中实现层叠布局的核心容器...', author: '鸿蒙开发者', time: '2分钟前', readTime: '8分钟' }, { id: 2, icon: '📋', tag: '列表', title: 'List 高性能长列表与分组吸顶实战', summary: '虚拟化渲染原理、ListItemGroup 分组、sticky 吸顶的完整实现。', content: 'List 是 ArkUI 中专为长列表场景设计的容器...', author: '鸿蒙开发者', time: '1小时前', readTime: '10分钟' }, { id: 3, icon: '🎠', tag: '轮播', title: 'Swiper 轮播图与页面滑动完整指南', summary: 'DotIndicator 自定义样式、SwiperController 编程翻页、卡片流预览效果。', content: 'Swiper 是 ArkUI 专为轮播图和画廊设计的滑动容器...', author: '鸿蒙开发者', time: '3小时前', readTime: '7分钟' }, { id: 4, icon: '📑', tag: '导航', title: 'Tabs 标签页布局:底部导航到顶部分类', summary: '底部导航栏实现、嵌套 Tabs、自定义 TabBar 样式完整示例。', content: 'Tabs 是 ArkUI 中实现标签页导航的专用组件...', author: '鸿蒙开发者', time: '昨天', readTime: '9分钟' }, { id: 5, icon: '🗂', tag: '侧边栏', title: 'SideBarContainer 侧边导航:大屏双栏布局实战', summary: 'Embed vs Overlay 模式、autoHide 自适应、自定义汉堡菜单。', content: 'SideBarContainer 是 ArkUI 专为平板大屏设计的导航容器...', author: '鸿蒙开发者', time: '2天前', readTime: '11分钟' }, ] // 路由表:名称 → 页面 Builder 的映射 @Builder pageMap(name: string, param: ESObject) { if (name === 'detail') { ArticleDetailPage({ pathStack: this.pathStack, articleId: (param as ArticleParam).articleId, articles: this.articles }) } } @Builder listItem(article: ArticleItem) { Row({ space: 12 }) { Text(article.icon) .fontSize(24) .width(48).height(48) .textAlign(TextAlign.Center) .backgroundColor('#f0f5ff') .borderRadius(12) Column({ space: 4 }) { Text(article.title) .fontSize(14) .fontWeight(FontWeight.Medium) .fontColor('#1a1a1a') .maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis }) Row({ space: 8 }) { Text(article.tag) .fontSize(11) .fontColor('#0066ff') .backgroundColor('#f0f5ff') .padding({ left: 6, right: 6, top: 2, bottom: 2 }) .borderRadius(4) Text(article.time).fontSize(11).fontColor('#aaa') Text(article.readTime + ' 阅读').fontSize(11).fontColor('#bbb') } } .layoutWeight(1) .alignItems(HorizontalAlign.Start) } .width('100%') .padding({ left: 16, right: 16, top: 14, bottom: 14 }) .backgroundColor('#ffffff') .border({ width: { bottom: 1 }, color: '#f5f5f5' }) .onClick(() => { // push 到详情页,传递参数 this.pathStack.pushPathByName('detail', { articleId: article.id } as ArticleParam) }) } build() { Navigation(this.pathStack) { // 列表页(根页面) Column({ space: 0 }) { // 搜索栏 Row({ space: 8 }) { Text('🔍').fontSize(16).fontColor('#888') Text('搜索文章...') .fontSize(14).fontColor('#bbb').layoutWeight(1) } .width('100%') .height(40) .padding({ left: 14, right: 14 }) .backgroundColor('#f5f5f5') .borderRadius(20) .margin({ left: 16, right: 16, top: 8, bottom: 4 }) // 文章列表 List() { ForEach(this.articles, (article: ArticleItem) => { ListItem() { this.listItem(article) } }) } .divider({ strokeWidth: 1, color: '#f8f8f8', startMargin: 76 }) .layoutWeight(1) .backgroundColor('#f8f8f8') } .width('100%') .height('100%') } .title('HarmonyOS 学习笔记') .navDestination(this.pageMap) .mode(NavigationMode.Stack) .titleMode(NavigationTitleMode.Mini) .toolbarConfiguration([ { value: '首页', icon: $r('sys.media.ohos_ic_public_home') }, { value: '设置', icon: $r('sys.media.ohos_ic_public_settings') }, ]) .width('100%') .height('100%') .backgroundColor('#f8f8f8') } } // ── 详情页组件 ── @Component struct ArticleDetailPage { pathStack: NavPathStack = new NavPathStack() articleId: number = 0 articles: ArticleItem[] = [] private getArticle(): ArticleItem { for (let i = 0; i < this.articles.length; i++) { if (this.articles[i].id === this.articleId) { return this.articles[i] } } return this.articles[0] } build() { NavDestination() { Scroll() { Column({ space: 16 }) { // 头部卡片 Column({ space: 12 }) { Text(this.getArticle().icon).fontSize(48) Text(this.getArticle().tag) .fontSize(12).fontColor('#0066ff') .backgroundColor('#f0f5ff') .padding({ left: 10, right: 10, top: 3, bottom: 3 }) .borderRadius(10) Text(this.getArticle().title) .fontSize(20).fontWeight(FontWeight.Bold).fontColor('#111').lineHeight(28) Row({ space: 12 }) { Text(this.getArticle().author).fontSize(13).fontColor('#888') Text(this.getArticle().time).fontSize(13).fontColor('#aaa') Text(this.getArticle().readTime + ' 阅读').fontSize(13).fontColor('#aaa') } } .width('100%').padding(20) .backgroundColor('#fff').borderRadius(12) .border({ width: 1, color: '#f0f0f0' }) .alignItems(HorizontalAlign.Center) // 摘要 Column({ space: 8 }) { Text('📌 摘要') .fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333') Text(this.getArticle().summary) .fontSize(14).fontColor('#666').lineHeight(22) } .width('100%').padding(16) .backgroundColor('#fff').borderRadius(12) .border({ width: 1, color: '#f0f0f0' }) .alignItems(HorizontalAlign.Start) // 正文 Column({ space: 10 }) { Text('📖 正文').fontSize(15).fontWeight(FontWeight.Bold).fontColor('#333') Text(this.getArticle().content + '更多内容持续撰写中,欢迎关注本系列教程。') .fontSize(14).fontColor('#555').lineHeight(22) } .width('100%').padding(16) .backgroundColor('#fff').borderRadius(12) .border({ width: 1, color: '#f0f0f0' }) .alignItems(HorizontalAlign.Start) Column().height(20) } .width('100%').padding({ left: 16, right: 16, top: 16, bottom: 16 }) } .width('100%').height('100%') } .title(this.getArticle().title) .backButtonIcon($r('sys.media.ohos_ic_back')) .onBackPressed(() => { this.pathStack.pop() return true }) } }关键知识点
1. NavPathStack 是路由核心
private pathStack: NavPathStack = new NavPathStack() // push:跳转到目标页,传参 this.pathStack.pushPathByName('detail', { id: 1 }) // pop:返回上一页 this.pathStack.pop() // popToRoot:返回到根页 this.pathStack.popToRoot() // 获取当前路由栈深度 this.pathStack.size()2. navDestination:注册路由表
@Builder pageMap(name: string, param: ESObject) { if (name === 'detail') { DetailPage({ pathStack: this.pathStack, id: (param as MyParam).id }) } } Navigation(this.pathStack) .navDestination(this.pageMap) // 传入路由表 Builder3. NavDestination:目标页面容器
每个目标页面必须用NavDestination包裹,它提供 AppBar、返回按钮等:
@Component struct DetailPage { pathStack: NavPathStack = new NavPathStack() build() { NavDestination() { // 页面内容 } .title('详情页') .onBackPressed(() => { this.pathStack.pop() return true // 返回 true 表示拦截了系统返回,由自己处理 }) } }4. NavigationMode 三种模式
| 模式 | 效果 | 适用场景 |
|---|---|---|
NavigationMode.Stack | 全屏堆叠(手机) | 手机应用 |
NavigationMode.Split | 左右分栏(平板) | 平板大屏 |
NavigationMode.Auto | 根据屏幕宽度自适应 | 通用(推荐) |
5. 参数传递
pushPathByName的第二个参数类型是ESObject(等同于object),接收方需要做类型断言:
// 推送方 this.pathStack.pushPathByName('detail', { articleId: 1 } as ArticleParam) // 接收方(NavDestination 对应的组件属性) articleId: number = 0 // 由 Navigation 框架注入小结
Navigation是 HarmonyOS 推荐的组件内路由方案,比router.push更灵活NavPathStack管理路由栈,pushPathByName/pop/popToRoot是核心操作navDestination(@Builder)注册路由表,建立名称到组件的映射- 目标页必须用
NavDestination包裹,并在onBackPressed中调用pop() NavigationMode.Auto自动在手机(Stack)和平板(Split)之间切换,是最佳实践