1. HarmonyOS页面路由(Router)
页面路由指在应用程序中实现不同页面之间的跳转和数据传递。HarmonyOS提供了Router模块,通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面。本文将从页面跳转、页面返回和页面返回前增加一个询问框几个方面介绍Router模块提供的功能。
1.1. 页面跳转
页面跳转是开发过程中的一个重要组成部分。在使用应用程序时,通常需要在不同的页面之间跳转,有时还需要将数据从一个页面传递到另一个页面。
1.1.1. 跳转模式
Router模块提供了两种跳转模式,分别是router.pushUrl()和router.replaceUrl()。这两种模式决定了目标页是否会替换当前页。
(1)router.pushUrl() :目标页不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用router.back()方法返回到当前页。
(2)router.replaceUrl() :目标页会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。
说明:页面栈的最大容量为32个页面。如果超过这个限制,可以调用router.clear()方法清空历史页面栈,释放内存空间。
1.1.2.实例模式
Router模块提供了两种实例模式,分别是Standard和Single。这两种模式决定了目标url是否会对应多个实例。
(1)Standard :标准实例模式,也是默认情况下的实例模式。每次调用该方法都会新建一个目标页,并压入栈顶。
(2)Single:单实例模式。即如果目标页的url在页面栈中已经存在同url页面,则离栈顶最近的同url页面会被移动到栈顶,并重新加载;如果目标页的url在页面栈中不存在同url页面,则按照标准模式跳转。
1.2. 场景
1.2.1.场景一
有一个主页(Home)和一个详情页(Detail),希望从主页点击一个商品,跳转到详情页。同时,需要保留主页在页面栈中,以便返回时恢复状态。这种场景下,可以使用pushUrl()方法,并且使用Standard实例模式(或者省略)。标准实例模式下,router.RouterMode.Standard参数可以省略。
javascript
private standardClick() {
router.pushUrl({
url: 'pages/myTool/RouterTwoPage' // 目标url
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
}
1.2.2.场景二
有一个登录页(Login)和一个个人中心页(Profile),希望从登录页成功登录后,跳转到个人中心页。同时,销毁登录页,在返回时直接退出应用。这种场景下,可以使用replaceUrl()方法,并且使用Standard实例模式(或者省略)。
javascript
private standardReplaceClick() {
router.replaceUrl({
url: 'pages/myTool/RouterTwoPage'
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke replaceUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke replaceUrl succeeded.');
})
}
1.2.3.场景三
有一个设置页(Setting)和一个主题切换页(Theme),希望从设置页点击主题选项,跳转到主题切换页。同时,需要保证每次只有一个主题切换页存在于页面栈中,在返回时直接回到设置页。这种场景下,可以使用pushUrl()方法,并且使用Single实例模式。
javascript
private singleClick() {
router.pushUrl({
url: 'pages/myTool/RouterTwoPage'
}, router.RouterMode.Single, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
}
1.2.4.场景四
有一个搜索结果列表页(SearchResult)和一个搜索结果详情页(SearchDetail),希望从搜索结果列表页点击某一项结果,跳转到搜索结果详情页。同时,如果该结果已经被查看过,则不需要再新建一个详情页,而是直接跳转到已经存在的详情页。这种场景下,可以使用replaceUrl()方法,并且使用Single实例模式。
javascript
private single2Click() {
router.replaceUrl({
url: 'pages/myTool/RouterTwoPage' // 目标url
}, router.RouterMode.Single, (err) => {
if (err) {
console.error(`Invoke replaceUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke replaceUrl succeeded.');})
}
1.2.5.场景五
如果需要在跳转时传递一些数据给目标页,则可以在调用Router模块的方法时,添加一个params属性,并指定一个对象作为参数。例如:
(1)RouterBean
javascript
export class RouterItemBean {
id?: string
title?: string
}
export class RouterBean {
mainId?: string;
routerItemBean?: RouterItemBean;
}
(2)带参跳转
javascript
private paramsClick() {
let paramsInfo: RouterBean = {
mainId: "123",
routerItemBean: {
id: "456",
title: "789",
}
};
router.pushUrl({
url: 'pages/myTool/RouterTwoPage', // 目标url
params: paramsInfo // 添加params属性,传递自定义参数
}, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
})
}
(3)在目标页中,可以通过调用Router模块的getParams()方法来获取传递过来的参数。例如:
javascript
aboutToAppear() {
try {
// 获取传递过来的参数对象
this.routerBean = (router.getParams() as RouterBean);
let mainId = this.routerBean.mainId;
let routerItem = this.routerBean.routerItem;
let id = routerItem?.id;
let title = routerItem?.title;
this.msg="获取传过来的数据:"+mainId+id+title
} catch (e) {
}
}
1.3.页面返回
当用户在一个页面完成操作后,通常需要返回到上一个页面或者指定页面,这就需要用到页面返回功能。在返回的过程中,可能需要将数据传递给目标页,这就需要用到数据传递功能。
1.3.1.方式一: 返回到上一个页面
这种方式会返回到上一个页面,即上一个页面在页面栈中的位置。但是,上一个页面必须存在于页面栈中才能够返回,否则该方法将无效。
javascript
private routerClick() {
//router.back(2)
router.back()
}
1.3.2.方式二:返回到指定页面
这种方式可以返回到指定页面,需要指定目标页的路径。目标页必须存在于页面栈中才能够返回。
javascript
private back2Click() {
router.back({
url: 'pages/myTool/RouterOnePage'
});
}
1.3.3.方式三:返回到指定页面,并传递自定义参数信息
这种方式不仅可以返回到指定页面,还可以在返回的同时传递自定义参数信息。这些参数信息可以在目标页中通过调用router.getParams()方法进行获取和解析。
javascript
private back3Click() {
let paramsInfo: RouterBean = {
mainId: "111",
routerItem: {
id: "222",
title: "333",
},
};
router.back({
url: 'pages/myTool/RouterOnePage',
params: paramsInfo
});
}
在目标页中,在需要获取参数的位置调用router.getParams()方法即可,例如在onPageShow()生命周期回调中:
javascript
onPageShow() {
try {
// 获取传递过来的参数对象
this.routerBean = (router.getParams() as RouterBean);
let mainId = this.routerBean.mainId;
let routerItem = this.routerBean.routerItem;
let id = routerItem?.id;
let title = routerItem?.title;
this.msg="获取传过来的数据:"+mainId+id+title
} catch (e) {
}
}
当使用router.back()方法返回到指定页面时,原栈顶页面(包括)到指定页面(不包括)之间的所有页面栈都将从栈中弹出并销毁。
另外,如果使用router.back()方法返回到原来的页面,原页面不会被重复创建,因此使用@State声明的变量不会重复声明,也不会触发页面的aboutToAppear()生命周期回调。如果需要在原页面中使用返回页面传递的自定义参数,可以在需要的位置进行参数解析。例如,在onPageShow()生命周期回调中进行参数解析。
1.4.完整代码
1.4.1.RouterBean.ets
javascript
export class RouterItem {
id?: string
title?: string
}
export class RouterArr {
array?:string[]
}
export class RouterBean {
mainId?: string;
routerItem?: RouterItem;
routerArr?: RouterArr;
}
1.4.2.RouterOnePage.ets
javascript
import { TitleBar } from '../../components/common/TitleBar'
import { router } from '@kit.ArkUI'
import { RouterParams } from '../../helper/RouterHelper'
import { BusinessError } from '@kit.BasicServicesKit'
import { Logger } from '../../utils/Logger'
import { RouterBean } from '../../bean/RouterBean'
@Extend(Button)
function buttonItem() {
.stateEffect(true)
.type(ButtonType.Normal)
.borderRadius(8)
.fontSize(17)
.backgroundColor($r('app.color.primary_green'))
.padding({
top: 8,
bottom: 8,
left: 70,
right: 70
})
.margin({
top: 15,
bottom: 15
})
}
@Entry
@Component
struct RouterOnePage {
@State pageTitle: string = "路由跳转"
private routerBean?: RouterBean
private msg: string = '3f3d4 '
aboutToAppear() {
try {
this.pageTitle = (router
.getParams() as RouterParams).title
} catch (e) {
}
}
onPageShow() {
try {
// 获取传递过来的参数对象
this.routerBean = (router.getParams() as RouterBean);
let mainId = this.routerBean.mainId;
let routerItem = this.routerBean.routerItem;
let id = routerItem?.id;
let title = routerItem?.title;
this.msg="获取传过来的数据:"+mainId+id+title
} catch (e) {
}
}
/**
* router.pushUrl()
*/
async routerClick() {
//router.pushUrl()
//目标页面不会替换当前页,而是压入页面栈。
// 这样可以保留当前页的状态,并且可以通过返回键
// 或者调用router.back()方法返回到当前页。
let options: router.RouterOptions = {
url: 'pages/myTool/RouterTwoPage',
params: new RouterParams("路由跳转", [12, 45, 78])
}
try {
await router.pushUrl(options)
} catch (err) {
console.info(` fail callback,
code: ${(err as BusinessError).code},
msg: ${(err as BusinessError).message}`)
}
}
/**
* router.replaceUrl()
*/
async replaceRouterClick() {
router.replaceUrl({ url: 'pages/myTool/RouterTwoPage' })
.catch((err: Error) => {
Logger.error(JSON.stringify(err));
})
}
/**
*有一个主页(Home)和一个详情页(Detail),
* 希望从主页点击一个商品,跳转到详情页。
* 同时,需要保留主页在页面栈中,以便返回时恢复状态。
* 这种场景下,可以使用pushUrl()方法,
* 并且使用Standard实例模式(或者省略)。
*/
private standardClick() {
router.pushUrl({
url: 'pages/myTool/RouterTwoPage' // 目标url
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
}
/**
* 有一个登录页(Login)和一个个人中心页(Profile),
* 希望从登录页成功登录后,跳转到个人中心页。
* 同时,销毁登录页,在返回时直接退出应用。
* 这种场景下,可以使用replaceUrl()方法,
* 并且使用Standard实例模式(或者省略)。
*/
private standardReplaceClick() {
router.replaceUrl({
url: 'pages/myTool/RouterTwoPage'
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error(`Invoke replaceUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke replaceUrl succeeded.');
})
}
/**
* 有一个设置页(Setting)和一个主题切换页(Theme),
* 希望从设置页点击主题选项,跳转到主题切换页。
* 同时,需要保证每次只有一个主题切换页存在于页面栈中,
* 在返回时直接回到设置页。这种场景下,可以使用pushUrl()方法,
* 并且使用Single实例模式。
*/
private singleClick() {
router.pushUrl({
url: 'pages/myTool/RouterTwoPage'
}, router.RouterMode.Single, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
}
/**
*有一个搜索结果列表页(SearchResult)和一个搜索结果详情页(SearchDetail),
* 希望从搜索结果列表页点击某一项结果,跳转到搜索结果详情页。
* 同时,如果该结果已经被查看过,则不需要再新建一个详情页,
* 而是直接跳转到已经存在的详情页。这种场景下,可以使用replaceUrl()方法,
* 并且使用Single实例模式。
*/
private single2Click() {
router.replaceUrl({
url: 'pages/myTool/RouterTwoPage' // 目标url
}, router.RouterMode.Single, (err) => {
if (err) {
console.error(`Invoke replaceUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke replaceUrl succeeded.');
})
}
/**
*如果需要在跳转时传递一些数据给目标页,则可以在调用Router模块的方法时,
* 添加一个params属性,并指定一个对象作为参数。例如:
*/
private paramsClick() {
let paramsInfo: RouterBean = {
mainId: "123",
routerItem: {
id: "456",
title: "789",
},
};
router.pushUrl({
url: 'pages/myTool/RouterTwoPage', // 目标url
params: paramsInfo // 添加params属性,传递自定义参数
}, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code},
message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
})
}
build() {
Column() {
TitleBar({ pageTitle: $pageTitle })
Button('路由跳转')
.buttonItem()
.onClick(this.routerClick)
Button('销毁跳转')
.buttonItem()
.onClick(this.replaceRouterClick)
Button('standard')
.buttonItem()
.onClick(this.standardClick)
Button('standardReplace')
.buttonItem()
.onClick(this.standardReplaceClick)
Button('single')
.buttonItem()
.onClick(this.singleClick)
Button('single2')
.buttonItem()
.onClick(this.single2Click)
Button('带参路由')
.buttonItem()
.onClick(this.paramsClick)
Text(this.msg)
.fontSize(18)
.fontColor($r('app.color.primary_font_title'))
.margin({ top: 20 })
}
.height('100%')
}
}
1.4.3.RouterTwoPage.ets
javascript
import { TitleBar } from '../../components/common/TitleBar'
import { router } from '@kit.ArkUI'
import { RouterParams } from '../../helper/RouterHelper'
import { BusinessError } from '@kit.BasicServicesKit'
import { Logger } from '../../utils/Logger'
import { RouterBean } from '../../bean/RouterBean'
@Extend(Button)
function buttonItem() {
.stateEffect(true)
.type(ButtonType.Normal)
.borderRadius(8)
.fontSize(17)
.backgroundColor($r('app.color.primary_green'))
.padding({
top: 8,
bottom: 8,
left: 70,
right: 70
})
.margin({
top: 15,
bottom: 15
})
}
@Entry
@Component
struct RouterTwoPage {
@State pageTitle: string = "路由跳转二"
private routerBean?: RouterBean
private msg: string = '3f3d4 '
aboutToAppear() {
try {
// 获取传递过来的参数对象
this.routerBean = (router.getParams() as RouterBean);
let mainId = this.routerBean.mainId;
let routerItem = this.routerBean.routerItem;
let id = routerItem?.id;
let title = routerItem?.title;
this.msg="获取传过来的数据:"+mainId+id+title
} catch (e) {
}
}
/**
* 返回到上一个页面
*/
private backClick() {
//router.back(2)
router.back()
}
/**
* 返回指定页面
*/
private back2Click() {
router.back({
url: 'pages/myTool/RouterOnePage'
});
}
/**
* 返回到指定页面,并传递自定义参数信息
*/
private back3Click() {
let paramsInfo: RouterBean = {
mainId: "111",
routerItem: {
id: "222",
title: "333",
},
};
router.back({
url: 'pages/myTool/RouterOnePage',
params: paramsInfo
});
}
build() {
Column() {
TitleBar({ pageTitle: $pageTitle })
Button('返回到上一个页面')
.buttonItem()
.onClick(this.backClick)
Button('返回指定页面')
.buttonItem()
.onClick(this.back2Click)
Button('传参返回')
.buttonItem()
.onClick(this.back3Click)
Text(this.msg)
.fontSize(18)
.fontColor($r('app.color.primary_font_title'))
.margin({ top: 20 })
}
.height('100%')
}
}