鸿蒙 组件导航Navigation 解析
HarmonyOS的Navigation组件是页面路由导航的根容器,提供三种显示模式(单栏、分栏、自适应),支持灵活的路由操作和参数传递。主要特点包括:支持组件级路由跳转、多端适配、丰富的标题栏和菜单栏样式。核心功能涵盖页面跳转(push/pop)、参数传递、路由拦截、生命周期管理,以及自定义转场动画等。通过NavPathStack管理路由栈,可实现页面跳转、替换、删除等操作。还支持跨包跳转和共享
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一. 概述
Navigation 是 HarmonyOS 中用于实现页面路由导航的根视图容器,一般作为页面的根容器使用。它支持在不同 Navigation 页面(NavDestination)间进行跳转,传递参数,并提供灵活的跳转栈操作。
核心特性
-
三种显示模式:单栏(Stack)、分栏(Split)、自适应(Auto)
-
组件级路由能力:实现自然流畅的转场体验
-
多端适配:自动适配窗口大小,大窗口下自动切换分栏模式
-
丰富的标题栏样式:提供更好的标题和内容联动效果
二. 显示模式
2.1 自适应模式(默认)
Navigation() {
// 页面内容
}
.mode(NavigationMode.Auto)
-
宽度阈值:API 9及以前 520vp,API 10及以后 600vp
-
自动切换:超过阈值时分栏模式,否则单栏模式
2.2 单栏模式
Navigation() {
// 页面内容
}
.mode(NavigationMode.Stack)
-
适用于窄屏设备
-
路由跳转时整个页面被替换
2.3 分栏模式
@Entry
@Component
struct SplitNavigationExample {
@State menuItems: ToolbarItem = {
'value': "功能",
'icon': "./image/function_icon.svg",
'action': () => {}
}
@Provide('navigationStack') navStack: NavPathStack = new NavPathStack()
private pageNumbers: number[] = [1, 2, 3];
@Builder
buildPageContent(pageName: string) {
if (pageName === "PageOne") {
FirstPage();
} else if (pageName === "PageTwo") {
SecondPage();
} else if (pageName === "PageThree") {
ThirdPage();
}
}
build() {
Column() {
Navigation(this.navStack) {
TextInput({ placeholder: '输入搜索内容...' })
.width("90%")
.height(40)
.backgroundColor('#FFFFFF')
List({ space: 12 }) {
ForEach(this.pageNumbers, (pageNum: number) => {
ListItem() {
Text("页面" + pageNum)
.width("100%")
.height(72)
.backgroundColor('#FFFFFF')
.borderRadius(24)
.fontSize(16)
.fontWeight(500)
.textAlign(TextAlign.Center)
.onClick(() => {
this.navStack.pushPath({ name: "Page" + pageNum });
})
}
}, (pageNum: number) => pageNum.toString())
}
.width("90%")
.margin({ top: 12 })
}
.title("应用主标题")
.mode(NavigationMode.Split)
.navDestination(this.buildPageContent)
.menus([
{
value: "", icon: "./image/search_icon.svg", action: () => {}
},
{
value: "", icon: "./image/add_icon.svg", action: () => {}
}
])
.toolbarConfiguration([this.menuItems, this.menuItems])
}
.height('100%')
.width('100%')
.backgroundColor('#F1F3F5')
}
}
三. 标题栏设置
3.1 Mini 模式
-
普通型标题栏,适用于一级页面不需要突出标题的场景
Navigation() {
// 内容
}
.titleMode(NavigationTitleMode.Mini)
3.2 Full 模式
-
强调型标题栏,适用于一级页面需要突出标题的场景
Navigation() {
// 内容
}
.titleMode(NavigationTitleMode.Full)
四. 菜单栏和工具栏
4.1 菜单栏设置
let menuItem: NavigationMenuItem = {
'value': "",
'icon': "./image/menu_icon.svg",
'action': () => {}
}
Navigation() {
// 内容
}
.menus([menuItem, menuItem, menuItem])
-
竖屏:最多显示3个图标,多余放入更多图标
-
横屏:最多显示5个图标
4.2 工具栏设置
let toolbarItem: ToolbarItem = {
'value': "操作",
'icon': "./image/tool_icon.svg",
'action': () => {}
};
let toolbarItems: ToolbarItem[] = [toolbarItem, toolbarItem];
Navigation() {
// 内容
}
.toolbarConfiguration(toolbarItems)
五. 路由操作
5.1 导航控制器初始化
@Entry
@Component
struct MainApp {
appNavigationStack: NavPathStack = new NavPathStack();
build() {
Navigation(this.appNavigationStack) {
// 应用内容
}
.title('应用主页')
}
}
5.2 页面跳转
// 普通跳转
this.navStack.pushPath({ name: "DetailPage", param: "页面参数" });
this.navStack.pushPathByName("DetailPage", "页面参数");
// 带返回回调的跳转
this.navStack.pushPathByName('DetailPage', "参数内容", (popInfo) => {
console.info('页面返回: ' + popInfo.info.name + ', 结果: ' + JSON.stringify(popInfo.result));
});
// 带错误处理的跳转
this.navStack.pushDestination({name: "DetailPage", param: "参数内容"})
.catch((error: BusinessError) => {
console.error(`跳转失败, 错误码: ${error.code}, 错误信息: ${error.message}`);
}).then(() => {
console.info('跳转成功');
});
5.3 页面返回
// 返回上一页
this.navStack.pop();
// 返回到指定页面
this.navStack.popToName("HomePage");
// 返回到指定索引页面
this.navStack.popToIndex(1);
// 清空栈返回到首页
this.navStack.clear();
5.4 页面替换和删除
// 页面替换
this.navStack.replacePath({ name: "NewPage", param: "新参数" });
this.navStack.replacePathByName("NewPage", "新参数");
// 页面删除
this.navStack.removeByName("OldPage");
this.navStack.removeByIndexes([1, 3, 5]);
this.navStack.removeByNavDestinationId("page123");
5.5 页面移动
// 移动页面到栈顶
this.navStack.moveToTop("ImportantPage");
this.navStack.moveIndexToTop(2);
六. 参数传递和获取
6.1 页面参数获取
@Component
struct DetailPage {
navigationStack: NavPathStack | undefined = undefined;
receivedParam: string = '';
build() {
NavDestination() {
Column() {
Text('接收到的参数: ' + this.receivedParam)
}
}
.title('详情页')
.onReady((context: NavDestinationContext) => {
this.navigationStack = context.pathStack;
this.receivedParam = context.pathInfo.param as string;
})
}
}
6.2 接收返回时传递的路由参数
class ReturnData {
message: string = '返回数据内容'
}
@Component
struct DataEntryPage {
build() {
NavDestination() {
Column() {
Button('返回并传递数据')
.onClick(() => {
// 返回操作
})
}
}
.onResult((data: Object) => {
if (data instanceof ReturnData) {
console.info('接收到返回数据: ' + (data as ReturnData).message);
return;
}
console.info('返回数据类型不匹配');
})
}
}
七. 路由拦截
this.navStack.setInterception({
willShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar",
operation: NavigationOperation, animated: boolean) => {
if (typeof to === "string") {
console.info("目标页面是导航首页");
return;
}
// 路由重定向示例
let target: NavDestinationContext = to as NavDestinationContext;
if (target.pathInfo.name === 'RestrictedPage') {
target.pathStack.pop();
target.pathStack.pushPathByName('LoginPage', null);
}
}
})
八. 子页面管理
8.1 标准页面类型
@Component
struct StandardPage {
@Consume('navigationStack') navStack: NavPathStack;
build() {
NavDestination() {
Column() {
Text('标准页面内容')
}
}
.title('标准页面')
.mode(NavDestinationMode.STANDARD)
}
}
8.2 弹窗页面类型
@Component
struct ModalDialogPage {
@Consume('navigationStack') navStack: NavPathStack;
build() {
NavDestination() {
Stack({ alignContent: Alignment.Center }) {
Column() {
Text("弹窗内容")
.fontSize(20)
.margin({ bottom: 100 })
Button("关闭弹窗")
.onClick(() => {
this.navStack.pop();
})
.width('40%')
}
.justifyContent(FlexAlign.Center)
.backgroundColor(Color.White)
.borderRadius(15)
.height('35%')
.width('85%')
}
.height("100%")
.width('100%')
}
.backgroundColor('rgba(0,0,0,0.6)')
.hideTitleBar(true)
.mode(NavDestinationMode.DIALOG)
}
}
九. 页面生命周期
-
aboutToAppear - 自定义组件创建后,build() 执行前
-
onWillAppear - NavDestination 创建后,挂载到组件树前
-
onAppear - 组件挂载到组件树时
-
onWillShow - 页面布局显示前
-
onShown - 页面布局显示完成
-
onActive - 页面处于激活态
-
onWillHide - 页面触发隐藏前
-
onInactive - 页面处于非激活态
-
onHidden - 页面触发隐藏后
-
onWillDisappear - 页面即将销毁前
-
onDisappear - 组件从组件树卸载时
-
aboutToDisappear - 自定义组件析构前
十. 页面转场动画
10.1 关闭系统转场
// 全局关闭
this.navStack.disableAnimation(true);
// 单次关闭
this.navStack.pushPath({ name: "NextPage" }, false);
this.navStack.pop(false);
10.2 自定义转场
// Navigation 级别自定义转场
Navigation(this.navStack) {
// 内容
}
.customNavContentTransition(() => {
// 返回自定义转场协议对象
})
// NavDestination 级别自定义转场
NavDestination() {
// 页面内容
}
.customTransition(() => {
// 返回页面转场代理
})
10.3 共享元素转场
// 起始页面
NavDestination() {
Column() {
Image($r('app.media.sharedImage'))
.geometryTransition('transitionId')
.width(100)
.height(100)
}
}
.title('起始页')
// 目标页面
NavDestination() {
Column() {
Image($r('app.media.sharedImage'))
.geometryTransition('transitionId')
.width(200)
.height(200)
}
}
.title('目标页')
// 跳转时启用动画
Button('跳转')
.onClick(() => {
this.getUIContext()?.animateTo({ duration: 800 }, () => {
this.navStack.pushPath({ name: 'TargetPage' }, false)
});
})
十一. 跨包跳转
11.1 系统路由表(推荐)
module.json5 配置:
{
"module": {
"routerMap": "$profile:route_config"
}
}
route_config.json 配置:
{
"routerMap": [
{
"name": "ProductDetail",//跳转页面名称
"pageSourceFile": "src/main/ets/pages/ProductDetail.ets",
//目标页在包内的路径,相对src目录的相对路径
"buildFunction": "ProductDetailBuilder",
//目标页的入口函数名称,必须以@Builder修饰
"data": {
"category": "electronics"
}
//应用自定义字段。可以通过配置项读取接口getConfigInRouteMap获取
}
]
}
页面构建函数:
@Builder
export function ProductDetailBuilder() {
ProductDetailPage();
}
@Component
struct ProductDetailPage {
navStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
// 页面内容
}
.title('商品详情')
.onReady((context: NavDestinationContext) => {
this.navStack = context.pathStack;
})
}
}
11.2 自定义路由表
// 动态 import 方式(推荐)
@Builder
buildPageRouter(pageName: string) {
if (pageName === "DynamicPage") {
DynamicImportHelper.loadDynamicPage();
}
}
Navigation(this.navStack) {
// 内容
}
.navDestination(this.buildPageRouter)
更多推荐



所有评论(0)