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

文档中心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75150.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为开源自研AI框架昇思MindSpore应用案例:基于MindSpore框架实现PWCNet光流估计

如果你对MindSpore感兴趣&#xff0c;可以关注昇思MindSpore社区 1 环境准备 1.进入ModelArts官网 云平台帮助用户快速创建和部署模型&#xff0c;管理全周期AI工作流&#xff0c;选择下面的云平台以开始使用昇思MindSpore&#xff0c;可以在昇思教程中进入ModelArts官网 创建…

虚幻基础:UI

文章目录 控件蓝图可以装载其他控件蓝图可以安装其他蓝图接口 填充&#xff1a;相对于父组件填充水平框尺寸—填充—0.5&#xff1a;改变填充的尺寸填充—0.5&#xff1a;改变与父组件的距离 锚点&#xff1a;相对于父组件的控件坐标系原点&#xff0c;屏幕比例改变时&#xff…

监控平台——SkyWalking部署

一、环境准备 先下载SkyWalking安装包&#xff0c;需要注意的是SkyWalking 版本在10.X以上使用的nacos-client是2.X&#xff0c;如果安装的Nacos版本是1.X就会存在兼容性的问题。由于本人使用的SpringBoot项目是2.7.X版本&#xff0c;安装的Nacos版本只能是1.X版本的&#xff…

热门索尼S-Log3电影感氛围旅拍LUTS调色预设 Christian Mate Grab - Sony S-Log3 Cinematic LUTs

热门索尼S-Log3电影感氛围旅拍LUTS调色预设 Christian Mate Grab – Sony S-Log3 Cinematic LUTs 我们最好的 Film Look S-Log3 LUT 的集合&#xff0c;适用于索尼无反光镜相机。无论您是在户外、室内、风景还是旅行电影中拍摄&#xff0c;这些 LUT 都经过优化&#xff0c;可为…

自动化工作流工具的综合对比与推荐

最近收到很多朋友私信我说&#xff1a;“刷短视频的时候&#xff0c;总是刷到自动化工作流的工具&#xff0c;有好多直播间都在宣传&#xff0c;不知道哪款工具好”。我花了点时间&#xff0c;做了一下测试&#xff0c;大家可以参考一下&#xff0c;以下内容&#xff1a; 以下…

fircrawl本地部署

企业内部的网站作为知识库给dify使用&#xff0c;使用fircrawl来爬虫并且转换为markdown。 ​ git clone https://github.com/mendableai/firecrawl.gitcd ./firecrawl/apps/api/ cp .env.example .env cd ~/firecrawl docker compose up -d 官方&#xff1a; https://githu…

day17 学习笔记

文章目录 前言一、数组的增删改查1.resize函数2.append函数3.insert函数4.delete函数5.argwhere函数6.unique函数 二、统计函数1.amax&#xff0c;amin函数2.ptp函数3.median函数4.mean函数5.average函数6.var&#xff0c;std函数 前言 通过今天的学习&#xff0c;我掌握了num…

CentOS 8 Stream 配置在线yum源参考 —— 筑梦之路

CentOS 8 Stream ISO 文件下载地址&#xff1a;http://mirrors.aliyun.com/centos-vault/8-stream/isos/x86_64/CentOS-Stream-8-20240603.0-x86_64-dvd1.isoCentOS 8 Stream 网络引导ISO 文件下载地址&#xff1a;http://mirrors.aliyun.com/centos-vault/8-stream/isos/x86_6…

网络原理-TCP/IP

网络原理学习笔记&#xff1a;TCP/IP 核心概念 本文是我在学习网络原理时整理的笔记&#xff0c;主要涵盖传输层、网络层和数据链路层的核心协议和概念&#xff0c;特别是 TCP, UDP, IP, 和以太网。 一、传输层 (Transport Layer) 传输层负责提供端到端&#xff08;进程到进…

EF Core 执行原生SQL语句

文章目录 前言一、执行查询&#xff08;返回数据&#xff09;1&#xff09; 使用 FromSqlRaw或 FromSqlInterpolated 方法&#xff0c;适用于 DbSet<T>&#xff0c;返回实体集合。2&#xff09;结合 LINQ 查询3&#xff09;执行任意原生SQL查询语句&#xff08;使用ADO.N…

Unity LOD Group动态精度切换算法(基于视锥+运动速度)技术详解

一、动态LOD技术背景与核心挑战 1. 传统LOD系统的局限 静态阈值切换&#xff1a;仅基于距离的切换在动态场景中表现不佳 视觉突变&#xff1a;快速移动时LOD层级跳变明显 性能浪费&#xff1a;静态算法无法适应复杂场景变化 对惹&#xff0c;这里有一个游戏开发交流小组&…

MyBatis复杂查询——一对一、一对多

目录 &#xff08;一&#xff09;复杂查询&#xff1a;1对1关系 【任务】数据库里有学生表(student)和学生证信息表(student_card)&#xff0c;表结构如下所示&#xff0c;要求使用MyBatis框架查询所有的学生信息以及每位学生的学生证信息 解决方案1&#xff1a;关联查询实现…

【服务端】使用conda虚拟环境部署Django项目

写在开头 为了与客户端的Deep search配合&#xff0c;需要整一个后台管理来保存和管理deep search的数据资料。选择前端框架Vue-Vben-Admin Django后台服务来实现这个项目。 废话结束&#xff0c;从零开始。。。。 一、环境搭建 1. 安装 Anaconda 下载 Anaconda&#xff1…

Python爬虫-爬取大麦网演出详情页面数据

前言 本文是该专栏的第50篇,后面会持续分享python爬虫干货知识,记得关注。 本文,笔者以大麦网平台为例。基于Python,实现获取演出详情页面的演出信息。 废话不多说,具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内…

多onnx模型导出合并调研(文本检测+方向分类+文本识别)

👑主页:吾名招财 👓简介:工科学硕,研究方向机器视觉,爱好较广泛… ​💫签名:面朝大海,春暖花开! 多onnx模型合并导出调研(文本检测+方向分类+文本识别) 引言1,尝试合并两个模型(文本方向分类+文本识别模型)(并行合并)(1)文本方向分类(2)文本识别模型(…

Flink介绍——实时计算核心论文之S4论文详解

引入 在上一篇我们对Flink的发展历史有了全局的了解&#xff0c;下面我们会通读几篇分布式实时处理相关的重要论文&#xff0c;从S4到Storm&#xff0c;再从MillWheel到Dataflow&#xff0c;最后到Flink。 通过深入梳理分布式实时处理技术的发展脉络&#xff0c;了解这些年技…

【商城实战(97)】ELK日志管理系统的全面应用

【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…

Linux系统-ls命令

一、ls命令的定义 Linux ls命令&#xff08;英文全拼&#xff1a;list directory contents&#xff09;用于显示指定工作目录下之内容&#xff08;列出目前工作目录所含的文件及子目录)。 二、ls命令的语法 ls [选项] [目录或文件名] ls [-alrtAFR] [name...] 三、参数[选项…

游戏被外挂攻破?金融数据遭篡改?AI反作弊系统实战方案(代码+详细步骤)

一、背景与需求分析 随着游戏行业与金融领域的数字化进程加速,作弊行为(如游戏外挂、金融数据篡改)日益复杂化。传统基于规则的防御手段已难以应对新型攻击,而AI技术通过动态行为分析、异常检测等能力,为安全领域提供了革命性解决方案。本文以游戏反作弊系统和金融数据安…

Node.js 路由 - 初识 Express 中的路由

目录 Node.js 路由 - 初识 Express 中的路由 1. 什么是路由&#xff1f; 2. 安装 Express 3. 创建 server.js 4. 运行服务器 5. 测试路由 5.1 访问主页 5.2 访问用户路由 5.3 发送 POST 请求 6. 结语 1. 什么是路由&#xff1f; 路由&#xff08;Routing&#xff09…