HarmonyOS-ArkUI Navigation (导航组件)-第一部分
导航组件主要实现页面间以及组件内部的界面跳转,支持在不同的组件间进行参数的传递,提供灵活的跳转栈操作,从而便捷的实现对不同页面的访问和复用。
我们之前学习过Tabs组件,这个组件里面也有支持跳转的方式,Navigation与Tabs最主要的区别就在于,Navigation里面含有一个跳转栈,且使用方式比Tabs更为复杂。这个相当于Android中Fragment的Navigation,异曲同工。下文中我们将会习得
- 导航组件的显示模式
- 导航组件的路由操作
- 子页面管理
- 跨包跳转
- 跳转动效
想要支持Navigation的栈式维护和跳转,需要在页面的build函数下,将Navigation作为根节点,也就是视图的根容器!要不然不行。并且,其跳转到的页面的根容器,一定要是NavDestination,否则即使跳转过去了,您的页面也是没有被渲染的,Navigation组件本身也提供了布局的结构,也可以供您进行设置。
Navigation体系的界面有两大部分构成
- 主页, 主页是所有导航的源头处,引发的第一个导航。我们的路由栈(
NavPathStack)也是在这个入口处进行设置,并由子页一层一层传递始终能够拿到这个句柄,来进行导航的。 - 子页, 就是被导航的页面。 子页也可以导航到其他的页面,主要就是得拿到那个唯一的路由栈(
NavPathStack)。
主页与子页之间的导航器,可以配合由 Provide 和 Consume 两个装饰器来进行传递,之后有案例,可以在代码中注意一下使用方式。
主页与子页内容构成图

未加任何复杂操作的主页(主要展示标题,菜单栏,工具栏的展示方式)
@Entry
@Component
struct NavigatePage {@State message: string = 'Hello World';@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}// 用这个@Provide装饰器装饰的变量,可以传递给子组件@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()build() {Column(){Navigation(this.pageInfos) {}//设置为分栏模式.mode(NavigationMode.Stack)//设置顶部标题.title("主标题")// 设置顶部菜单栏.menus([{value: "菜单1", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单2", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单3", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单4", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单5", icon: $r('app.media.ic_icon'), action: () => {}}])//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
}
![]()
界面设置基础
设置页面显示模式
在上文中的代码中,有一个属性叫 mode, 它是用来设置页面模式的。 页面模式总共分为三种
- Stack:单栏
- Split: 分栏
- Auto:自适应
Split模式
上图中我们设置的主页面是 Stack模式,为单栏的效果。其实也可以设置分栏模式,其值设置为 Split即可。
下图为设置为分栏模式的样子,好让大家有个印象。
@Entry
@Component
struct NavigatePage {@State message: string = 'Hello World';@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}// 用这个@Provide装饰器装饰的变量,可以传递给子组件@Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()build() {Column(){Navigation(this.pageInfos) {}//设置为分栏模式.mode(NavigationMode.Split)//设置顶部标题.title("主标题")...省略其他设置代码...}.height('100%').width('100%').backgroundColor('#F1F3F5')}
}

Auto模式
Auto模式是让导航页面自己计算大小,屏幕宽就双列展示,不宽就单列显示。

设置标题模式
如果您写了一个Navigation组件,没有设置title的情况下这个title是不会显示的。但是如果设置了,从上图中你也看到了,这个标题是不是有些,太大了🥶。Navigetion组件可以通过titleMode属性设置标题栏的模式。titleMode属性的值为:
- Free:默认值,我试着是和Full是一样的效果,官网API没看出来啥区别。遇见区别之后再补修一下文章。
- Full:强调型标题栏,用于以及界面需要图书标题的场景.
- Mini:普通标题栏模式,用于一级页面不需要突出标题的场景。
Full模式

mini模式

设置菜单栏
菜单栏的展示行为
菜单栏位置是下方红圈中区域。

当设备处于竖屏模式下,最多展示3个图标,为横屏模式下,最多展示5个图标。这里需要注意的是,这个展示的情况是与设备所处的横竖屏状态有关。
设置菜单栏
菜单栏通过Navigation组件的menus属性进行设置。其声明格式如下:
menus(value: Array<NavigationMenuItem> | CustomBuilder): NavigationAttribute;
其参数可以使一个数组,也可以是一个自定义构建函数。
采用数组设置menus
build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus([{value: "菜单1", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单2", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单3", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单4", icon: $r('app.media.ic_icon'), action: () => {}},{value: "菜单5", icon: $r('app.media.ic_icon'), action: () => {}}])//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
采用自定义构建函数设置menus
@Builder myMenus(){Row(){Text('菜单1')Text('菜单2')Text('菜单3')Text('菜单4')Text('菜单5')}}build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus(this.myMenus)//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}

设置工具栏
工具栏是下图中所示的位置,如果不进行设置,就不会展示。工具栏的设置由toolBarConfiguration属性进行设置。

toolBarConfiguration的声明如下文所示:
toolbarConfiguration(value: Array<ToolbarItem> | CustomBuilder, options?: NavigationToolbarOptions): NavigationAttribute;
其参数可以是一个数组,也可以是一个自定义构建函数。具体写法与menus非常类似。
@State TooTmp:ToolbarItem = {value: 'func', icon: $r('app.media.ic_ok')}build() {Column(){Navigation(this.pageInfos) {TextInput({placeholder: 'search'}).width("90%").margin({top: 20,}).backgroundColor('#FFFFFF')List({space: 20}) {ForEach(this.arr, (item:number) => {ListItem() {Text("Page" + item).width("100%").height(72).backgroundColor('#FFFFFF').borderRadius(24).fontSize(16).fontWeight(500).textAlign(TextAlign.Center).onClick(() => {this.pageInfos.pushPath({name: "NavDestinationTitle" + item})})}}, (item:number) => item.toString())}.width('90%').margin({top: 12})}//设置为分栏模式.mode(NavigationMode.Stack).navDestination(this.pageMap)//设置顶部标题.title("主标题").titleMode(NavigationTitleMode.Full)// 设置顶部菜单栏.menus(this.myMenus)//设置底部工具栏.toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp])}.height('100%').width('100%').backgroundColor('#F1F3F5')}
路由操作
我们在Navitgation构建的时候,传递了一个参数。如图所示:

上图中NavPathStack是我们这个小节学习的重点,这个对象必须在Navigation构建的时候传入。Navigation整个体系下所有和路由相关的操作都是由NavPathStack提供的方法进行,这个对象主要就是用于页面的管理。其功能主要如下:
- 页面跳转
- 页面返回
- 页面替换
- 页面删除
- 参数获取,数据传递。
- 路由拦截
- ...
我们在页面进行操作之前,是有必要稍微了解下,导航组件完成完整功能的构成成分。以及与我们之前写的代码的对应部分(红色虚线)。好在心里有个蓝图。

页面跳转的一切基础,就是首先Navigation对应的页面栈NavPathStack您得提供。这个对象会在Navigation初始化的时候被传入,您可以用ArkUI中已经提供的类,也可以自己写一个子类,创建一个对象实例,传入即可。
@Entry
@Component
struct Index {// 创建一个页面栈对象并传入NavigationpageStack: NavPathStack = new NavPathStack()build() {Navigation(this.pageStack) {}.title('主标题')}
}
页面跳转
NavPathStack通过Push相关的接口去实现页面跳转的功能,主要分为三类
- 普通跳转
- 带返回回调的跳转
- 带错误码的跳转,跳转结束会触发异步回调,返回错误码信息。
普通跳转
this.pageInfos.pushPath({name: "NavDestinationTitle" + item, param: ''})
this.pageInfos.pushPathByName("NavDestinationTitle" + item, '')
带返回回调的跳转
跳转时可以添加onPop回调,能在页面出栈时获取返回信息。不知道为什么我自己写的不会返回。这个我调试的表现跟官网不一样。
this.pageInfos.pushPathByName("NavDestinationTitle" + item, '', (popInfo) => {hilog.info(DOMAIN_NAVIGATE, TAG, 'Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result))
})
带错误码的跳转
跳转结束触发异步回调,返回错误码信息。当被导航的界面打开的时候就会回调success
this.pageInfos.pushDestination({name: "NavDestinationTitle" + item, param: ''}).catch((error: BusinessError) => {hilog.error(DOMAIN_NAVIGATE, TAG, 'error Name= ' + error.name + " code=" + error.code + " message=" + error.message + " data=" + error.data)}).then(()=> {hilog.info(DOMAIN_NAVIGATE, TAG, "open success")})
或者这样写也可以
this.pageInfos.pushDestinationByName( "NavDestinationTitle" + item, '').catch((error: BusinessError) => {hilog.error(DOMAIN_NAVIGATE, TAG, 'error Name= ' + error.name + " code=" + error.code + " message=" + error.message + " data=" + error.data)}).then(()=> {hilog.info(DOMAIN_NAVIGATE, TAG, "open success")})
})
页面返回
NavPathStack通过pop相关接口去实现页面返回功能。
@Component
export struct PageOne {@State message: string = 'PageOne';@Consume('pageInfos') pageStack: NavPathStackbuild() {// 注意这里必须加上NavDestination方法,否则导航有效果但是界面展示不出来.NavDestination(){RelativeContainer() {Text(this.message + ' content').id('PageOne').fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).alignRules({center: { anchor: '__container__', align: VerticalAlign.Center },middle: { anchor: '__container__', align: HorizontalAlign.Center }}).onClick(() => {this.message = 'Welcome';})}.height('100%').width('100%').backgroundColor(Color.Red)}.title(this.message).onBackPressed(()=>{const popInfo = this.pageStack.pop(); //弹出栈顶元素console.log('pop' + '返回值' + JSON.stringify(popInfo))return true;})}
}
另外还有几个其他的接口
// 返回到上一页
this.pageStack.pop()
//返回到上一个pageOne页
this.pageStack.popToName('pageOne')
// 返回到索引为1的页面
this.pageStack.popToIndex(1)
// 返回到根首页,也就是主页
this.pageStack.clear()
页面替换
NavPathStack通过Replace相关接口去实现页面的替换功能。
.onBackPressed(()=>{// 注意此时的name,名称要是在RouterMap中注册的那些文字,否则找不到要替换哪一个的const info = this.pageStack.replacePath({name: "NavDestinationTitle1", param: ""})console.log('pop' + '返回值' + JSON.stringify(info))return true;})
// 将栈顶页面替换为PageOne
this.pageStack.replacePath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.replacePathByName("PageOne", "PageOne Param")
// 带错误码的替换,跳转结束会触发异步回调,返回错误码信息
this.pageStack.replaceDestination({name: "PageOne", param: "PageOne Param"}).catch((error: BusinessError) => {console.error(`Replace destination failed, error code = ${error.code}, error.message = ${error.message}.`);}).then(() => {console.info('Replace destination succeed.');})
页面删除
NavPathStack通过Remove相关接口实现页面的删除。
// 删除栈中name为PageOne的所有页面
this.pageStack.removeByName("PageOne")
// 删除指定索引的页面
this.pageStack.removeByIndexes([1,3,5])
// 删除指定id的页面
this.pageStack.removeByNavDestinationId("1");
移动页面
移动页面指的是,移动栈中的某指定页面到栈顶的位置。
// 移动栈中name为PageOne的页面到栈顶
this.pageStack.moveToTop("PageOne");
// 移动栈中索引为1的页面到栈顶
this.pageStack.moveIndexToTop(1);
获取参数
NavPathStack中的获取参数是比较有意思的,因为它可以按照页面获取参数,比如您现在在pageTwo中,也是可以搞到pogeOne中的参数的。这个就会数据中心的实现提供了很多的便利。
NavPathStack提供了一系列get操作去获取参数。
// 获取栈中所有页面name集合
this.pageStack.getAllPathName()
// 获取索引为1的页面参数
this.pageStack.getParamByIndex(1)
// 获取PageOne页面的参数
this.pageStack.getParamByName("PageOne")
// 获取PageOne页面的索引集合
this.pageStack.getIndexByName("PageOne")
路由拦截
NavPathStack提供了setInterception方法,用于设置Navigation跳转的拦截回调, 该方法需要传入一个NavigationInterception对象,该对象包含三个参数。
| willShow | 页面跳转前回调,此时可以修改操作栈中的行为 |
| didShow | 页面跳转后回调,此时不可以修改操作栈中的行为。 |
| modeChange | 单双栏显示发生变化时回调,主要是横竖屏切换的时候。 |
下方代码为,当跳转到pageTwo的时候,我们拦截,使其转向pageOne。
aboutToAppear(): void {this.pageInfos.setInterception({/*** willShow需要格外注意的是, 当A跳转至B, B又跳转至A的时候, A的willShow也会被调用的,因为A的确是即将被展示了.* 我们在理逻辑的时候,除了注意将要跳转的界面,也要注意将要返回的界面.*/willShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar, operation: NavigationOperation, isAnimated: boolean) => {// 当为导航主页的时候,这里的to是一个string类型,内容为 "navBar"if (typeof to === 'string') {let toStr:string = to as stringhilog.warn(DOMAIN_NAVIGATE, TAG, 'navigation home page! to=' + to)return}// 当为子导航页的时候,这里的to是NavDestinationContext类型// 修改操作栈,发现是PageTwo的跳转,就改成PageOnelet target: NavDestinationContext = to as NavDestinationContexthilog.info(DOMAIN_NAVIGATE, TAG, 'common page! to=' + JSON.stringify(target))if (target.pathInfo.name === 'NavDestinationTitle2') {target.pathStack.pop()target.pathStack.pushPath({name: 'NavDestinationTitle1'})}},didShow: (from: NavDestinationContext | NavBar, to: NavDestinationContext | NavBar, operation: NavigationOperation, isAnimated: boolean) => {hilog.debug(DOMAIN_NAVIGATE, TAG, "didShow callback")},modeChange: (mode: NavigationMode) => {hilog.debug(DOMAIN_NAVIGATE, TAG, "modeChange callback")}})}
子页面

子页面对应的是NavDestination组件,是Navigation体系中子页面部分的根容器,用于承载子页面的一些特殊属性和生命周期等。它可以设置独立的菜单栏和标题栏等属性,这类属性的设置方法与Navigation是一致的。
NavDestination的页面显示模式
NavDestination设置页面显示模式的属性,也叫 mode。通过设置mode属性,可以满足不同页面的诉求。
页面显示类型
- 标准类型:NavDestination的默认显示模式就是标准类型,此时mode = NavDestinationMode.STANDARD 。 标准类型的子页面生命周期跟随在NavPathStack页面栈中的位置变化而变化。
- 弹窗类型:NavDestination设置mode为 NavDestinationMode.DIALOG类型时,代表当前子页面是弹窗类型,此时整个NavDestination默认透明显示,并且它的消失和出现并不会影响下层标准类型的显示和生命周期,两者是可以同时显示的。 弹窗默认动效是从低向上平滑弹出。
@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackbuild() {NavDestination(){RelativeContainer() {RelativeContainer(){Text("这是一个弹窗").id('title').fontSize(20).alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).margin({top: 80}).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center)Button("退出").fontSize(18).alignRules({top: {anchor: 'title', align: VerticalAlign.Bottom},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).width('30%').margin({top: 50}).onClick(()=>{this.pageStack.pop()})}.alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End},bottom: {anchor: '__container__', align: VerticalAlign.Bottom}}).height('30%').width('80%').backgroundColor(Color.White).borderRadius(20)}.height('100%').width('100%').borderRadius(20)}.hideTitleBar(true) //关掉标题栏区域.backgroundColor('rgba(0, 0, 0, 0.3)').mode(NavDestinationMode.DIALOG) //指定为弹窗模式}
}

页面生命周期

页面监听和查询
为了方便组件与页面解耦,在NavDestination子页面定义的自定义组件可以通过全局方法监听或查询到页面的一些状态信息。
页面信息查询
自定义组件提供了quertNavDestinationInfo方法。可以在NavDestination内部查询到当前页面的信息,返回值是NavDestination,如果查询不到则返回undefined。
import { JSON } from "@kit.ArkTS"@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackbuild() {NavDestination(){RelativeContainer() {RelativeContainer(){Text("这是一个弹窗").id('title').fontSize(20).alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).margin({top: 80}).fontWeight(FontWeight.Bold).textAlign(TextAlign.Center)BackButton()}.alignRules({top: {anchor: '__container__', align: VerticalAlign.Top},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End},bottom: {anchor: '__container__', align: VerticalAlign.Bottom}}).height('30%').width('80%').backgroundColor(Color.White).borderRadius(20)}.height('100%').width('100%').borderRadius(20)}.hideTitleBar(true) //关掉标题栏区域.backgroundColor('rgba(0, 0, 0, 0.3)').mode(NavDestinationMode.DIALOG) //指定为弹窗模式}
}@Component
struct BackButton{@Consume('pageInfos') pageStack: NavPathStacknavDInfo:NavDestinationInfo | undefined;aboutToAppear(): void {// navDInfo = {"navigationId":"","name":"NavDestinationTitle4","state":4,// "index":0,"param":"NavDestinationTitle4","navDestinationId":"0"}this.navDInfo = this.queryNavDestinationInfo();console.log("navDInfo = " + JSON.stringify(this.navDInfo))}build() {Button("退出").fontSize(18).alignRules({top: {anchor: 'title', align: VerticalAlign.Bottom},start: {anchor: '__container__', align: HorizontalAlign.Start},end: {anchor: '__container__', align: HorizontalAlign.End}}).width('30%').margin({top: 50}).onClick(()=>{this.pageStack.pop()})}
}
页面状态监听
通过observer.on('navDestinationUpdate')提供的注册接口可以注册NavDestination生命周期变化的监听,使用方式如下:
import { JSON } from "@kit.ArkTS"
import { uiObserver } from "@kit.ArkUI"@Preview
@Component
export struct PageFourDialog {@Consume('pageInfos') pageStack: NavPathStackaboutToAppear(): void {uiObserver.on('navDestinationUpdate', (data: NavDestinationInfo)=>{console.log("navDestinationUpdate data=" + JSON.stringify(data))})}
...

监听切换页面的行为
aboutToAppear(): void {uiObserver.on('navDestinationUpdate', (data: NavDestinationInfo)=>{console.log("navDestinationUpdate data=" + JSON.stringify(data))})// 页面切换状态回调uiObserver.on('navDestinationSwitch', this.getUIContext(), (data:uiObserver.NavDestinationSwitchInfo) =>{console.log('navDestinationSwitch data=' + JSON.stringify(data))})}

页面转场
Navigation默认提供了页面的转场动画,通过页面栈操作时会触发不同的转场效果。如果不想要,可以设置关闭系统转场动画,同时您也可以进行自定义转场,以及共享元素转场的能力。
关闭转场
关闭转场有关闭全局转场和开启子页面时临时设置关闭转场动画。首先将关闭全局的转场。用到了NavPathStack的disableAnimation接口。
aboutToAppear(): void {this.pageInfos.disableAnimation(true)}
当设置关闭动画的时候,转场会显得很突兀🙃。根据特定需求谨慎设置吧。
临时设置关闭转场-单次关闭
NavPathStack中提供的Push, Pop, Replace等接口中可以设置animated参数,默认为true,表示带有转场动画,如果您需要这次操作中没有动画,则设置为false即可。
pageStack: NavPathStack = new NavPathStack()this.pageStack.pushPath({ name: "PageOne" }, false)
this.pageStack.pop(false)
跨包动态路由
我们首先看看之前写的代码, 在主导航页的时候,顶部出现了子页的import语句:

这种方式我们称之为静态路由跳转,这种方式能完成功能,但是存在缺陷
- 耦合过多,如果页面不出现这些import语句,在编译执行的时候,就不用处理这些连带的资源。
- 正是因为上个原因,会导致首页加载时间过长。
动态路由设计的初衷旨在解决多模块(HAR/HSP)能够复用相同的逻辑,实现业务模块之间的解耦,同时支持路由功能的扩展与整合。
系统路由表
上文提到过,动态路由是要解决多模块复用,解耦。系统路由表借助了Stage模型配置文件机制,在配置文件中添加路由表信息。这样的话,我们在配置文件中录入的就是路由表信息,也就是表示路由信息的字符串配置。之后代码中打开页面,仅仅指出路由表中配置的那些信息,系统就会找解析好的配置信息,动态调用配置好的方法。这个时候再引入上文中提到的那些静态资源也不迟。从而实现了页面懒加载。
让配置文件知道路由表的存在
很简单,在每个模块的module.json5 添加您的路由表配置即可。 module.json5是Stage开发模型中定义好的配置文件,每一个模块在创建之初都会新建一个这样的文件。
{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone"],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages", //这个是我们设置Pages的配置"routerMap": "$profile:router_map", //这个是我们路由导航的文件"abilities": [...],}
}
创建路由文件
很简单,看您模块mian_pages页面在哪里放着,我们的router_map文件就在哪里放着。因为就是同样的套路,连引用方式都是一样的,都是$profile:xxxx找到的。创建一个新文件如下所示:

完善路由配置文件
{"routerMap" : [{"name": "PageOne", "pageSourceFile": "src/main/ets/pages/PageOne.ets","buildFunction": "open","data": {"description": "this is PageOne"}}]
}
- name: 跳转页面名称
- pageSourceFile: 跳转目标页的包内路径,相对于src的路径。
- buildFunction: 跳转目标页的入口函数名称,必须要用@Builder 装饰器修饰,且位置位于所指文件顶层。才能找到
- data: 应用自定义字段,可以通过配置项读取接口,
getConfigRouteMap获取
在代码文件中补全配置文件中提到的入口函数

跳转代码

最后将原来文件中那些没必要的import, 设置RoutMap属性的代码都删掉。就完成解耦了。
自定义路由表
链接中是自定义路由表的实现方式。
参考文章:
CommonAppDevelopment/common/routermodule/README_AUTO_GENERATE.md · HarmonyOS-Cases/Cases - Gitee.com
文档中心
相关文章:
HarmonyOS-ArkUI Navigation (导航组件)-第一部分
导航组件主要实现页面间以及组件内部的界面跳转,支持在不同的组件间进行参数的传递,提供灵活的跳转栈操作,从而便捷的实现对不同页面的访问和复用。 我们之前学习过Tabs组件,这个组件里面也有支持跳转的方式,Navigati…...
【磁盘扩容】linux磁盘扩容
一、新磁盘分区 1、新磁盘在接入服务器后,很好辨认 使用fdisk -l命令,查看: 或者使用 lsblk -f 其中sdb,sdc, sda都是挂载硬盘,sr0为DVD光盘,很明显sdc没有进行任何的挂载,确定sdc为新磁盘 2、格式化新…...
字典翻转教学
第1关:创建大学英语四级单词字典 任务描述 本关任务:编写一个能创建大学英语四级单词字典的小程序。 相关知识 为了完成本关任务,你需要掌握: 1.创建空字典 2.字典中增加元素 3.字典视图 4.字典排序 创建空字典 空的大括号{}和…...
详解CountDownLatch底层源码
大家好,我是此林。 今天来分享一下CountDownLatch的底层源码。 CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的线程之间同步工具类,主要用于协调多个线程的执行顺序。其核心思想是通过计数器实现线程间的"等待-唤醒"机制&#…...
Gitee批量删除仓库
Gitee批量删除仓库 文章目录 Gitee批量删除仓库生成一个GiteeToken通过Python调用Gitee API参考文档 生成一个GiteeToken 右上角下拉->设置->安全设置->私人令牌->生成新令牌,注意将令牌保存(只会出现一次) 通过Python调用Gite…...
AI 时代,我们该如何写作?
当ChatGPT/DeepSeek能在几秒钟内产出一篇文章,而且生成能力日益精进,你是否也曾思考,我还能做什么? 当2024年AI开始进入人们的视野,我在CSDN 上的博客也悄然发生了变化,以前一篇文章发布后,阅读…...
day16 学习笔记
文章目录 前言一、广播机制二、数组遍历1.for循环2.nditer函数 三、数组操作1.reshape函数2.flat属性3.flatten函数4.revel函数5.数组转置6.升维与降维7.数组的连接与分割8.数组运算 前言 通过今天的学习,我进一步掌握了更多numpy的语法知识 一、广播机制 广播&am…...
Python基于EdgeTTS库文本转语音
EdgeTTS,支持粤语等各种方言,无需部署无需Key,完全免费,太香了 因为其底层是使用微软 Edge 的在线语音合成服务,所以不需要下载任何模型,甚至连 api_key 都给你省了,简直不要太良心~ 关键是&a…...
Day43 | 129. 求根节点到叶节点数字之和、1382. 将二叉搜索树变平衡、100. 相同的树
129. 求根节点到叶节点数字之和 题目链接:129. 求根节点到叶节点数字之和 - 力扣(LeetCode) 题目难度:中等 代码: class Solution {List<Integer> pathnew ArrayList<>();int res0;public int sumNumb…...
MFC案例:利用计时器(Timer)动态绘制正弦曲线
这是一个基于对话框的MFC程序,运行效果是在只画出I、IV象限的坐标系中绘制出红、蓝、绿各相差PI/2的三条正弦曲线,计时器运行一个周期曲线在X轴移动一个像素(对应1度),Y轴显示正弦值(150个像素代表1&#x…...
解析 HTML 网站架构规范
2025/3/28 向全栈工程师迈进! 一、网页基本的组成部分 网页的外观多种多样,但是除了全屏视频或游戏,或艺术作品页面,或只是结构不当的页面以外,都倾向于使用类似的标准组件。 1.1页眉 通常横跨于整个页面顶部有一…...
Kubernetes》》K8S》》Deployment 、Pod、Rs 、部署 nginx
Deployment deployment文档说明 kubectl get rs,deployment,pods 删除pod 、deployment 、service # 如果只删除pod,deployment会自动重建,所以应该先删除deployment。 # 下面演示的是删除所有deployment,可以指定只删除某个 # 删除所有…...
链表(C++)
这是本人第二次学习链表,第一次学习链表是在大一上的C语言课上,首次接触,感到有些难;第二次是在大一下学习数据结构时(就是这次),使用C再次理解链表。同时,这也是开启数据结构学习写…...
nginx 设置隐藏版本号
Nginx默认会在Server头里包含版本信息,比如“nginx/1.18.0”,这可能存在安全隐患,因为攻击者知道了版本号后,可以针对特定版本的漏洞进行攻击。所以,隐藏版本号是一个常见的安全措施。 可通过在http块里加上server_to…...
29_项目
目录 http.js 1、先注册账号 register.html 2、再登录 login.html 3、首页 index.html 4 详情 details.html cart.html css index.css register.css details.css 演示 进阶 http.js let baseURL "http://localhost:8888"; let resgiterApi baseURL &…...
排序算法1--插入排序
目录 1.常见排序算法 2.排序算法的预定函数 2.1交换函数 2.2测试算法运行时间的函数 3.插入排序 3.1直接插入排序 3.2希尔排序 3.3插入排序的时间复杂度分析 4.总结 1.常见排序算法 我将分别讲解五种排序算法,但是不代表只有五种固定的代码,之后…...
PolarDB数据库表恢复实战指南:通过控制台恢复表的完整操作流程
在数据库运维过程中,表数据恢复是一项常见且关键的操作。本文将详细介绍如何通过阿里云PolarDB控制台从备份中恢复特定表,并通过表重命名操作确保业务平稳过渡。 一、背景介绍 在日常数据库运维中,我们可能会遇到以下场景需要从备份中恢复表: 表数据被误删或损坏需要获取历…...
业之峰与宏图智能战略携手,开启家装数字化新篇章
3月8日,业之峰装饰集团董事长张钧携高管团队与宏图智能董事长庭治宏及核心团队,在业之峰总部隆重举行了战略合作签约仪式,标志着双方将携手探索业之峰的数字化转型之路,共同推动家装行业的变革与发展。 近年来,家装行业…...
matplotlib标题比x,y轴字体大,明明标题字体更大?
原始代码: plt.xlabel(训练轮次(Epochs), fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗 plt.ylabel(R值, fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗…...
【云原生】docker 搭建单机PostgreSQL操作详解
目录 一、前言 二、前置准备 2.1 服务器环境 2.2 docker环境 三、docker安装PostgreSQL过程 3.1 获取PostgreSQL镜像 3.2 启动容器 3.2.1 创建数据卷目录 3.2.2 启动pg容器 3.3 客户端测试连接数据库 四、创建数据库与授权 4.1 进入PG容器 4.2 PG常用操作命令 4.2…...
解决 Gradle 构建错误:Could not get unknown property ‘withoutJclOverSlf4J’
解决 Gradle 构建错误:Could not get unknown property ‘withoutJclOverSlf4J’ 在构建 Spring 源码或其他基于 Gradle 的项目时,可能会遇到如下错误: Could not get unknown property withoutJclOverSlf4J for object of type org.gradle…...
免费使用!OpenAI 全量开放 GPT-4o 图像生成能力!
2025年3月26日,OpenAI正式推出GPT-4o原生图像生成功能,这一更新不仅标志着多模态AI技术的重大突破,更引发了全球AI厂商的激烈竞争。从免费用户到企业开发者,从创意设计到科学可视化,GPT-4o正在重塑图像生成的边界。本文…...
jetson orin nano super AI模型部署之路(三)stable diffusion部署
先看一下部署后的界面和生成的图片。 在jetson orin nano super上部署stable diffusion比较简单,有现成的docker image和代码可用。 docker image拉取 使用的docker image是dustynv/stable-diffusion-webui,对于jetson orin nano super的jetpack6.2来说…...
深入理解:阻塞IO、非阻塞IO、水平触发与边缘触发
深入理解:阻塞IO、非阻塞IO、水平触发与边缘触发 在网络编程和并发处理中,理解不同的 I/O 模型和事件通知机制至关重要。本文将深入探讨阻塞IO(Blocking IO)、非阻塞IO(Non-Blocking IO)、水平触发&#x…...
WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试
WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程中继转发Relay媒体数据的turnserver的测试 WebRTC的ICE之TURN协议的交互流程和中继转发Relay媒体数据的turnserver的测试前言一、TURN协议1、连接Turn Server 流程①…...
HTTP---基础知识
天天开心!!! 文章目录 一、HTTP基本概念1. 什么是HTTP,又有什么用?2. 一次HTTP请求的过程3.HTTP的协议头4.POST和GET的区别5. HTTP状态码6.HTTP的优缺点 二、HTTP的版本演进1.各个版本的应用场景2、注意要点 三、HTTP与…...
Redis6数据结构之List类型
redis的List类型底层结构是双向链表,插入删除时间复杂度O(1)快,查找为O(n)慢。 应用场景:简单队列、最新评论列表、非实时排行榜(定时计算榜单,如笔记本日销榜单)。 常用命令: lpush将一个或多个值从左边…...
DeepSeek接入飞书多维表格,效率起飞!
今天教大家把DeepSeek接入飞书表格使用。 准备工作:安装并登录飞书;可以准备一些要处理的数据,确保数据格式正确,如 Excel、CSV 等,也可直接存储到飞书多维表格。 创建飞书多维表格:打开飞书,点…...
[FGPA基础学习]分秒计数器的制作
分秒计数器设计 本次实验内容为:DE2-115板子上用 Verilog编程实现一个 分秒计数器,并具备按键暂停、按键消抖功能 一、系统架构设计 顶层模块划分 顶层模块(top) ├── 按键消抖模块(key_debounce) ├…...
【Spring Boot 与 Spring Cloud 深度 Mape 之十】体系整合、部署运维与进阶展望
【Spring Boot 与 Spring Cloud 深度 Mape 之十】体系整合、部署运维与进阶展望 #微服务实战 #Docker #Kubernetes #SpringSecurity #OAuth2 #分布式事务 #Seata #ServiceMesh #总结 #SpringCloud #SpringBoot 系列终章:经过前九篇 [【深度 Mape 系列】] 的系统学习…...
