ArkUI Navigation:页面一多就别只靠 router
【摘要】 今天我们用 Navigation 做一个组件内的页面跳转。 刚开始写页面时,全局 router 的 pushUrl 很顺手。但当一个页面里面本来就有一组子页面,比如设置页、项目详情、二级说明页,一直用全局 router 跳来跳去,结构会越来越散。这个时候我们可以把这组子页交给 Navigation 管。 先准备一个导航
ArkUI Navigation:页面一多就别只靠 router
今天我们用 Navigation 做一个组件内的页面跳转。
刚开始写页面时,全局 router 的 pushUrl 很顺手。但当一个页面里面本来就有一组子页面,比如设置页、项目详情、二级说明页,一直用全局 router 跳来跳去,结构会越来越散。这个时候我们可以把这组子页交给 Navigation 管。
先准备一个导航栈
Navigation 需要一个 NavPathStack:
private pathStack: NavPathStack = new NavPathStack()
这一步很关键。NavPathStack 不是全局对象,它跟着当前 Navigation 走。你可以把它理解成:这一组组件导航自己的页面栈。
用 navDestination 映射子页
我们在 Demo 里准备了两个目标页:
.navDestination(this.navigationMap)
navigationMap 根据传进来的 name 决定展示哪个 NavDestination。这篇先用单文件里的 builder 映射,省掉系统路由表和跨包路由这些额外内容。
点击后压入子页
按钮里调用:
this.pathStack.pushPathByName(item.routeName, new NavigationDemoPageParam(item.title, item.desc, item.tag))
这里和全局 router 的页面跳转不一样。pushPathByName 压入的是 NavDestination,不是 main_pages.json 里的 @Entry 页面。
另外记住几个边界:
router管@Entry页面跳转,Navigation管组件内的NavDestination。Navigation花括号里的首页内容不在NavPathStack里,真正入栈的是子页。- 参数只放轻量数据,目标页仍然要准备缺省展示。
- 每个
Navigation自己持有自己的NavPathStack,不要几个导航容器混着用一个来源不清楚的栈。
运行后看什么
打开这个案例页后,你会看到两个卡片。点击任意卡片,会进入对应的 NavDestination;在子页里点击按钮,会调用同一个 pathStack.pop() 回到导航首页。
你平时更常遇到哪种情况:全局页面跳转太多,还是一个页面内部子页太多?后面如果要继续写 Navigation 进阶,我会优先从这些真实场景里挑。
完整代码
class NavigationDemoTopic {
routeName: string
title: string
desc: string
tag: string
color: string
bgColor: string
constructor(routeName: string, title: string, desc: string, tag: string, color: string, bgColor: string) {
this.routeName = routeName
this.title = title
this.desc = desc
this.tag = tag
this.color = color
this.bgColor = bgColor
}
}
class NavigationDemoPageParam {
title: string
desc: string
tag: string
constructor(title: string, desc: string, tag: string) {
this.title = title
this.desc = desc
this.tag = tag
}
}
@Entry
@Component
struct ArkUINavigationDemo {
private pathStack: NavPathStack = new NavPathStack()
private topics: NavigationDemoTopic[] = [
new NavigationDemoTopic('NavigationDetail', '项目详情', '进入一个 NavDestination,看看组件导航怎么压入子页。', 'pushPathByName', '#2563EB', '#EFF6FF'),
new NavigationDemoTopic('NavigationRules', '导航边界', '把 router 和 Navigation 的职责分开,页面结构会清楚很多。', 'NavPathStack', '#0F766E', '#F0FDFA')
]
@Builder
navigationMap(name: string, param: Object) {
if (name === 'NavigationDetail') {
NavigationDetailPage({ param: param as NavigationDemoPageParam })
} else if (name === 'NavigationRules') {
NavigationRulesPage({ param: param as NavigationDemoPageParam })
}
}
@Builder
topicCard(item: NavigationDemoTopic) {
Column({ space: 10 }) {
Row() {
Text(item.tag)
.fontSize(12)
.fontColor(item.color)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor('#FFFFFF')
.borderRadius(12)
Blank()
}
.width('100%')
Text(item.title)
.fontSize(20)
.fontWeight(700)
.fontColor('#0F172A')
Text(item.desc)
.fontSize(14)
.fontColor('#475569')
.lineHeight(21)
Button() {
Text('进入子页')
.fontSize(14)
.fontColor('#FFFFFF')
}
.width(120)
.height(38)
.borderRadius(19)
.backgroundColor(item.color)
.onClick(() => {
this.pathStack.pushPathByName(item.routeName, new NavigationDemoPageParam(item.title, item.desc, item.tag))
})
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.padding(16)
.backgroundColor(item.bgColor)
.borderRadius(8)
}
@Builder
noteRow(title: string, desc: string) {
Row({ space: 10 }) {
Text(title)
.width(92)
.fontSize(13)
.fontWeight(700)
.fontColor('#0F172A')
Text(desc)
.layoutWeight(1)
.fontSize(13)
.fontColor('#475569')
.lineHeight(20)
}
.width('100%')
.alignItems(VerticalAlign.Top)
}
build() {
Navigation(this.pathStack) {
Scroll() {
Column({ space: 16 }) {
Text('ArkUI Navigation')
.width('100%')
.fontSize(28)
.fontWeight(800)
.fontColor('#0F172A')
Text('Navigation 是组件导航容器。它管理的是 NavDestination,不是 main_pages.json 里的 @Entry 页面。')
.width('100%')
.fontSize(14)
.fontColor('#475569')
.lineHeight(22)
Column({ space: 10 }) {
this.noteRow('导航首页', 'Navigation 花括号里的内容是导航首页,它自己不在 NavPathStack 里。')
this.noteRow('子页入栈', '点击按钮后,pushPathByName 会把对应 NavDestination 压入这个 Navigation 的栈。')
this.noteRow('职责边界', 'router 负责页面级跳转;Navigation 更适合页面内部的一组子页。')
}
.width('100%')
.padding(14)
.backgroundColor('#F8FAFC')
.borderRadius(8)
ForEach(this.topics, (item: NavigationDemoTopic) => {
this.topicCard(item)
}, (item: NavigationDemoTopic) => item.routeName)
}
.width('100%')
.padding(20)
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
.title('Navigation')
.titleMode(NavigationTitleMode.Mini)
.mode(NavigationMode.Auto)
.navDestination(this.navigationMap)
}
}
@Component
struct NavigationDetailPage {
param: NavigationDemoPageParam = new NavigationDemoPageParam('项目详情', '进入一个 NavDestination。', 'pushPathByName')
private pathStack: NavPathStack = new NavPathStack()
build() {
NavDestination() {
Column({ space: 16 }) {
Text(this.param.tag)
.fontSize(13)
.fontColor('#2563EB')
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor('#EFF6FF')
.borderRadius(12)
Text(this.param.title)
.fontSize(26)
.fontWeight(800)
.fontColor('#0F172A')
Text(this.param.desc)
.fontSize(15)
.fontColor('#475569')
.lineHeight(23)
Text('这个页面不是一个新的 @Entry 页面,而是 Navigation 管理的 NavDestination。返回时调用同一个 pathStack.pop()。')
.fontSize(14)
.fontColor('#64748B')
.lineHeight(22)
.padding(14)
.backgroundColor('#F8FAFC')
.borderRadius(8)
Button() {
Text('返回导航首页')
.fontSize(15)
.fontColor('#FFFFFF')
}
.width('100%')
.height(44)
.borderRadius(22)
.backgroundColor('#2563EB')
.onClick(() => {
this.pathStack.pop()
})
}
.width('100%')
.height('100%')
.padding(20)
.alignItems(HorizontalAlign.Start)
.backgroundColor('#FFFFFF')
}
.title(this.param.title)
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
@Component
struct NavigationRulesPage {
param: NavigationDemoPageParam = new NavigationDemoPageParam('导航边界', '把职责分开。', 'NavPathStack')
private pathStack: NavPathStack = new NavPathStack()
@Builder
ruleItem(title: string, desc: string) {
Column({ space: 6 }) {
Text(title)
.fontSize(16)
.fontWeight(700)
.fontColor('#0F172A')
Text(desc)
.fontSize(13)
.fontColor('#475569')
.lineHeight(20)
}
.width('100%')
.padding(14)
.backgroundColor('#F8FAFC')
.borderRadius(8)
}
build() {
NavDestination() {
Column({ space: 12 }) {
Text(this.param.title)
.fontSize(26)
.fontWeight(800)
.fontColor('#0F172A')
this.ruleItem('一个 Navigation 一个栈', 'NavPathStack 跟着 Navigation 走,不要把几个导航容器混用一个不清楚来源的栈。')
this.ruleItem('导航首页不入栈', 'Navigation 里的首页内容只是容器内容,push 后进入栈的是 NavDestination。')
this.ruleItem('别和 router 混成一团', '这一组子页用 Navigation 管理;跨 @Entry 页面跳转再考虑 router。')
Button() {
Text('我知道了')
.fontSize(15)
.fontColor('#FFFFFF')
}
.width('100%')
.height(44)
.borderRadius(22)
.backgroundColor('#0F766E')
.onClick(() => {
this.pathStack.pop()
})
}
.width('100%')
.height('100%')
.padding(20)
.alignItems(HorizontalAlign.Start)
.backgroundColor('#FFFFFF')
}
.title(this.param.title)
.onReady((context: NavDestinationContext) => {
this.pathStack = context.pathStack
})
}
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)