Harmony路由原理

一个Ability里面的多个page可通过router进行切换。

页面内

页面内跳转是指所跳转的页面在同一个Ability内部,它们之间的跳转可以使用Router或者Navigator的方式

页面间

页面间跳转是指所跳转的页面属与不同的Ability,这种跳转需要借助featureAbility(featureAbility模块的startAbility()方法)实现
featureAbility有如下方法:

declare namespace featureAbility {
/**省略部分方法*/
//打开指定 Ability 。默认第一个
function startAbility(parameter: StartAbilityParameter, callback: AsyncCallback<number>): void;
function startAbility(parameter: StartAbilityParameter): Promise<number>;
function startAbilityForResult(parameter: StartAbilityParameter, callback: AsyncCallback<AbilityResult>): void;
function startAbilityForResult(parameter: StartAbilityParameter): Promise<AbilityResult>;
}

featureAbility使用方式:

  1. 创建targetAbility
  2. 在pages/index.ets中引入featureAbility(import featureAbility from ‘@ohos.ability.featureAbility’)
  3. 通过featureAbility.startAbility()打开目标ability
//pages/index.ets中在触发事件中使用
featureAbility.startAbility({
want: {
// 目标Ability所在的bundleName,也就是config.json里配置的bundleName
bundleName: "com.example.myapplication",
// 目标Ability的全路径
abilityName: "com.example.myapplication.targetAbility"
// 添加uri参数,指定打开创建targetAbility下的second页面。
uri: "pages/second"
}
})
.then((data) => {
console.info('Operation successful. Data: ' + JSON.stringify(data))
})
.catch((error) => {
console.error('Operation failed. Cause: ' + JSON.stringify(error));
})
  1. 修改SettingAbility的默认首页面

页面内路由实现方式

  • Navigator本质上是对 Router 的封装
  • 只能包含一个子组件
  • 页面布局的根容器,用于设置页面标题栏、工具栏以及菜单栏
  • 提供了 CustomBuilder 模式来自定义展示样式
    Navigation有如下属性:
declare class NavigationAttribute extends CommonMethod<NavigationAttribute> {
//menus、toolBar
menus(value: Array<NavigationMenuItem> | CustomBuilder): NavigationAttribute;
toolBar(value: object | CustomBuilder): NavigationAttribute;
title(value: string | CustomBuilder): NavigationAttribute;
subTitle(value: string): NavigationAttribute;
hideTitleBar(value: boolean): NavigationAttribute;
hideBackButton(value: boolean): NavigationAttribute;
titleMode(value: NavigationTitleMode): NavigationAttribute;
hideToolBar(value: boolean): NavigationAttribute;
onTitleModeChange(callback: (titleMode: NavigationTitleMode) => void): NavigationAttribute;
}
  • 生命周期承载在NavDestination组件上,以组件事件的形式开放
  • 可分为三类:自定义组件生命周期(aboutToAppear和aboutToDisappear,Navigation外面的自定义组件)、通用组件生命周期(OnAppear和OnDisappear)、自有生命周期(独有,共6个)
    Navigation生命周期
周期方法 作用
aboutToAppear 在创建自定义组件后,执行其build()函数之前执行(NavDestination创建之前),允许在该方法中改变状态变量,更改将在后续执行build()函数中生效。
onWillAppear NavDestination创建后,挂载到组件树之前执行,在该方法中更改状态变量会在当前帧显示生效。
onAppear 通用生命周期事件,NavDestination组件挂载到组件树时执行。
onWillShow NavDestination组件布局显示之前执行,此时页面不可见(应用切换到前台不会触发)。
onShown NavDestination组件布局显示之后执行,此时页面已完成布局。
onWillHide NavDestination组件触发隐藏之前执行(应用切换到后台不会触发)。
onHidden NavDestination组件触发隐藏后执行(非栈顶页面push进栈,栈顶页面pop出栈或应用切换到后台)。
onWillDisappear NavDestination组件即将销毁之前执行,如果有转场动画,会在动画前触发(栈顶页面pop出栈)。
onDisappear 通用生命周期事件,NavDestination组件从组件树上卸载销毁时执行。
aboutToDisappear 自定义组件析构销毁之前执行,不允许在该方法中改变状态变量。

动态路由表

  1. src/main/resources/base/profile中创建文件route_map.json
  2. 在route_map.json中添加如下信息
{
"routerMap": [
{
"name": "PageTest",
"pageSourceFile": "src/main/ets/pages/PageTest.ets",
"buildFunction": "PageTestBuilder"
}

]
}
  1. module.json5中注册路由
{
"module": {
"routerMap": "$profile:route_map"
}
}
  1. Index Page
import {PageTestBuilder} from './PageTest'
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
navPath:NavPathStack = new NavPathStack()
build() {
Column(){
Navigation(this.navPath){
Button("Switch to Page Test")
.onClick(()=>{
this.navPath.pushPath({ name: 'PageTest' })
})
}

}
}
}
  1. Target Page
@Builder
export function PageTestBuilder(){
PageTest()
}
@Component
export struct PageTest{
build() {
NavDestination() {
Column(){
Button('PageTest')
}
}
}
}

路由启动模式

this.navPath.pushPath({
name:"TargetPage"
},
{
launchMode:LaunchMode.MOVE_TO_TOP_SINGLETON
}
)

  1. 默认:将被Push的路由,即使已在路由栈中,仍会继续添加
  2. LaunchMode.MOVE_TO_TOP_SINGLETON:将从r3跳转到路由r2,若栈中为{r1,r2,r3},则将r2置于栈顶,此时栈为{r1,r3,r2}

使用示例

最小示例:

@Entry
@Component
struct Index {
//API10以上
@Entry
@Component
struct Index {
//API10及以上
//创建路由栈
@Provide('navPath') navPath:NavPathStack = new NavPathStack()

build() {
Navigation(this.navPath){
Column(){
Button('switch page2')
.onClick(()=>{
this.navPath.pushPath({
name:'404'
})
})
Button('404')
.onClick(()=>{
this.navPath.pushPath({
name:'404'
})
})
}
}
.navDestination(this.NavDes)

}
@Builder
NavDes(name:string,params:object){
if(name === "page2")
NavDestination(){
Text('page2')
}
else {
NavDestination(){
page3()
}
}
}
}
@Component
struct page3{
@Consume('navPath') navPath:NavPathStack
build() {
Column(){
Button('404')
.onClick(()=>{
//pushPath会入栈,然this.navPath.replacePath不会,只会替换页面
this.navPath.pushPath({
name: 'page2'
})
//以下两个联合使用可以返回首页
//this.navPath.clear()
//this.navPath.pop()
})
}
}
}

传值示例

//
.onClick(()=>{
this.navPath.replacePath({
name: 'page2',
param:{
name:"mmmm"
} as params
})
}
)
interface params{
name:string
}

//从page2返回page1传参
////page1
this.navPath.pushPath({
name: 'page3',
param:{
name:"mmmm"
} as params,
onPop:(info)=>{
this.name = JSON.stringify(info) //一个object,即info1和info2
}
})
////page2
this.navPath.pop({
info1: "",
info2, "",
})

样式示例


router(现已不推荐)

  • @Entry修饰的组件才可使用
    路由配置
//方法1
//pushUrl(options: RouterOptions, callback: AsyncCallback<void>): void
import { router } from '@kit.ArkUI';
router.pushUrl({
url:"pages/PostPage"
})

//方法2
//pushNamedRoute
import { router } from '@kit.ArkUI';


//方法3
//最后在事件中调用routePage()函数即可
import { router } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit'
async routePage() {
let options:router.RouterOptions = {
url: 'pages/PostPage',
}
try {
// 建议使用this.getUIContext().getRouter().pushUrl()
await router.pushUrl(options)
console.log('kkk')
} catch (err) {
console.info(` fail callback, code: ${(err as BusinessError).code}, msg: ${(err as BusinessError).message}`)
}
}


//resouces/base/profile/main_pages.json中添加路径
//需从同pages层级的文件夹下开始索引
{
"src": [
"pages/Index",
"pages/PostPage",
"Components/Notification"
]
}

相关函数

//获取发起跳转的页面往当前页传入的参数
router.getParams(): Object
//获取当前在页面栈内的页面数量
router.getLength(): string
//获取栈顶的状态信息
router.getState(): RouterState
//通过索引值获取对应页面的状态信息
router.getStateByIndex(index: number): RouterState | undefined
//清空页面栈中的所有历史页面,仅保留当前页面作为栈顶页面
router.clear(): void
//返回指定的页面,会删除当前页面与指定页面之间的所有页面
router.back(index: number, params?: Object): void
//跳转到指定的命名路由页面
router.pushNamedRoute(options: NamedRouterOptions, mode: RouterMode, callback: AsyncCallback<void>): void
//用指定的命名路由页面替换当前页面,并销毁被替换的页面
router.replaceNamedRoute(options: NamedRouterOptions, mode: RouterMode, callback: AsyncCallback<void>): void

路由启动模式


传值示例

//index.ets
router.pushUrl({url:"pages/Page2",params:{key:"111",val:"222"} as Params})
interface Params{
key:string
val:string
}

//Page2.ets
let info = router.getParams() as Params
this.message = info.key

aboutToAppear(): void { //接收Page1传值
let info = router.getParams() as Params
this.message = info.key
}
onPageShow(): void { //接收Page3返回值
let info = router.getParams() as Params
this.message = info.key
}

//Page3.ets
router.back({ //向Page2返回值
url:"pages/Page2",
params:{key:'zzz',val:"xxx"} as Params
})

Ability之间

  1. 配置
    EntryAbility->pages/ABtest
    TestAbility->pages/PayIndex
    传值:ABtest->PayIndex
//module.json5
{
"module": {
"mainElement": "EntryAbility",
}
//通过new一个新的Ability生成
"abilities": [
{
"name": "TestAbility",
}
]
}
//配置初次进入的入口(EntryAbility.ets)
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/ABtest', (err) => {
}
}

  1. 跨Ability传值
//ABTest.ets,用于跳转到PayIndex。需注册路由(new一个page)
@Entry
Button("switch to pay")
.onClick(()=>{
this.pay()
})
}
pay(){
let MainContext = getContext() as common.UIAbilityContext
let want:Want = {
//目标包和目标Ability
bundleName:"com.example.course",//项目根目录下AppScope/app.json5中的包名
abilityName:"TestAbility",
parameters:{
payId:Date.now()
}
}
//向TestAbility传值
MainContext.startAbility(want)
}
//待跳转的TestAbility
//接收ABTest的传值(onCreate只能执行一次,onNewWant可多次)
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
if(want.parameters){
let info = want.parameters["payId"] as number
AppStorage.setOrCreate("payId",info)
}
//跳转到PayIndex
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/PayIndex', (err) => {
}
}
//PayIndex.ets,接收从传递的数据。需注册路由(new一个page)
@Entry
@StorageProp("payId") payId:number = 0
  1. 接收返回值
//PayIndex.ets向ABTest.ets传递返回值
Button("返回值")
.onClick(()=>{
let MainContext = getContext(this) as common.UIAbilityContext
MainContext.terminateSelfWithResult({
resultCode:1,
want:{
bundleName:"com.example.course",
abilityName:"EntryAbility",
parameters:{
type:"success"
}
}
})
})
//ABTest.ets接收返回值
async pay(){
let MainContext = getContext() as common.UIAbilityContext
let want:Want = {
bundleName:"com.example.course",
abilityName:"TestAbility",
uri:"",
deviceId:"",
parameters:{
payId:Date.now()
}
}
// MainContext.startAbility(want)
let result = await MainContext.startAbilityForResult(want)
if(result.resultCode == 1){
this.message = result.want!.parameters!["type"] as string
}
}

  1. 跳转
//ABtest.ets
async pay(){
let MainContext = getContext() as common.UIAbilityContext
let want:Want = {
bundleName:"com.example.course",
abilityName:"TestAbility",
//应用内TestAbility下面的某个页面路由
uri:"Index",
//分布式,发送到其他设备
deviceId:"",
parameters:{
payId:Date.now()
}
}
// MainContext.startAbility(want)
let result = await MainContext.startAbilityForResult(want) //1.promise参数表明可用await;2.注意外面得有async
if(result.resultCode == 1){
this.message = result.want!.parameters!["type"] as string
}
}
//TestAbility
export default class TestAbility extends UIAbility {
url:string = ''
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
//默认uri为pages/Index
let uri = "pages/Index"
//接收从ABtest.ets传送的uri,判断具体要转送到哪个页面路由
switch (this.url){
case "Index":
uri = "pages/Page2"
break
}
//第一个参数 改为uri
windowStage.loadContent(uri, (err) => {
}
}