HarmonyOS 页面路由(Router)

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%')
  }
}
相关推荐
whysqwhw1 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw2 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw4 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw5 小时前
鸿蒙音频编码
harmonyos
whysqwhw5 小时前
鸿蒙音频解码
harmonyos
whysqwhw5 小时前
鸿蒙视频解码
harmonyos
whysqwhw6 小时前
鸿蒙视频编码
harmonyos
ajassi20006 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos
前端世界7 小时前
在鸿蒙应用中快速接入地图功能:从配置到实战案例全解析
华为·harmonyos
江拥羡橙8 小时前
【基础-判断】HarmonyOS提供了基础的应用加固安全能力,包括混淆、加密和代码签名能力
安全·华为·typescript·harmonyos