【HarmonyOS】ArkUI - 页面路由

一、概念

页面路由是指在应用程序中实现不同页面之间的跳转和数据传递。

案例:第一次使用某个购物应用,打开时肯定会是一个登录页,在登录成功以后,会跳转到首页,然后可能会去搜索,就会进入到搜索列表页,接着呢如果搜索到某一个感兴趣的商品A,点击,就会进入到商品A的详情页,到现在为止已经访问了好多不同的页面,并且在它们之间完成了跳转,那我之前访问过的页面都去哪里了?是不是全部被销毁了?其实并没有,我们在页面跳转之间访问过的所有页面,都会被 HarmonyOS 保存到页面栈的空间当中。页面栈顾名思义就是保存页面的栈结构空间,栈结构是先进后出,所以呢,我们最早访问的登录页就被压到了栈的最底层,而当前正在访问的商品A的详情页就在栈顶。也就是说,谁在栈顶,当前显示的就是谁的页面。那么 HarmonyOS 为啥要把这些访问过的页面保存起来,而不是直接销毁掉呢?放在这里不占用内存吗?这其实是我们的页面功能需要去用到这些历史页面。一般商品详情页都会有返回按钮,当点击返回按钮,应该返回到之前访问过的搜索列表页,有了页面栈,想要实现这个功能就非常简单了。只需要在点击返回时,把栈顶的这个页面移除,这样一来,紧挨着栈顶的搜索列表页就成为了新的栈顶页面,现在显示的就是搜索列表页,从而也就实现了返回的效果。如果现在想做页面跳转,过程就相反,比如在搜索列表页点击商品B,只需要把商品B的详情页创建出来,然后压入栈里,现在栈顶就是商品B的详情页,从而实现了跳转效果。简单来讲,如果想实现创建页面,就压入栈;如果想实现返回,就把栈顶页面弹出栈,即可。

  1. 页面栈的最大容量:

    页面栈的最大容量上限为 32 个页面。就是说如果我们不断的去访问新的页面,往栈里压入页面,可能就会达到上限,这时候再想访问这个页面,再想往里面去压栈,就会报错,这时候就不得不去调用 router.clear() 方法去清空页面栈,就会把历史页面干掉,释放内存。但是,一旦把历史页面干掉,再想返回前一个页面就访问不了了。所以 router.clear() 要慎重使用。我们在开发的过程中一定要想办法控制页面栈里的页面数量,不要让它达到上限,而不是说等达到上限去清空。

  2. 怎么去控制页面栈里的页面数量呢?就要使用页面栈不同的跳转行为模式。Router 有两种页面跳转模式:

    • router.pushUrl():目标页不会替换当前页,而是压入页面栈。比如当前在商品A的详情页,如果点击商品B的图片,就需要压入栈,就需要创建一个商品B的页面,把商品B的详情页压入栈顶,这时候,原有的商品A的详情页不会被移除,而是压到栈的内部,成为一个历史页面,因此点返回按钮,用 router.back() 就会返回到历史页面商品A的详情页。但是,这种实现方式会导致栈里的页面会越来越多。

    • router.replaceUrl():目标页替换当前页,当前页会被销毁并释放资源。也就是说从商品A的详情页跳转到商品B的详情页,这时候商品A的详情页就变成了历史页,会直接被销毁,而不是在栈内保存。这样一来,内存就节省出来,但是如果想从商品B的详情页返回到商品A的详情页,就返回不了了。

  3. 何时使用 router.pushUrl(),何时又使用 router.replaceUrl() 呢?

    举个例子,比如说,我们的登录页,只有在第一次打开的时候才需要,只要不退出,就不用再登录。所以登录页基本上就访问一次,而且也不需要返回,登录页保存在历史页面栈里没有任何意义,所以在登录成功以后,跳转到首页时,就可以使用 router.replaceUrl() 把登录页销毁;如果我们从首页跳转到搜索列表页,如果这时候点返回,返回到首页,所以我们的首页应该在页面栈里保存,作为一个历史页面,就要用 router.pushUrl()

  4. 如果商品A的详情页和商品B的详情页来回切换,如果用到 router.pushUrl(),会导致页面栈的容量一会儿就满了,就要用到页面实例模式,Router 有两种页面实例模式:

    • Standard:标准实例模式,每次跳转都会新建一个目标页并压入栈顶。默认就是这种模式。

    • Single:单实例模式,顾名思义,每一个页面只会存在一份,如果目标页已经在栈中,则离栈顶最近的同Url页面会被移动到栈顶并重新加载。

结合合适的跳转模式(router.pushUrl()router.replaceUrl())和实例模式(StandardSingle),就能够控制页面栈里的页面数量,避免达到上限。

二、Router API 用法

  1. 首先要导入 HarmonyOS 提供的 Router 模块:

    ts 复制代码
    import router from '@ohos.router';
  2. 然后利用 router 实现跳转、返回等操作:

    ts 复制代码
    router.pushUrl(
      {
        url: 'pages/PageA',
        params: { id: 1 }
      },
      router.RouterMode.Single,
      err => {
        if (err) {
          console.log('路由失败。')
        }
      }
    )
    • RouterOptions

      • url:目标页面路径
      • params:传递的参数(可选)
    • RouterMode

      • Standard:标准实例模式
      • Single:单实例模式
    • 异常响应回调函数

      • 错误码 100001:内部错误,可能是渲染失败
      • 错误码 100002:路由地址错误
      • 错误码 100003:路由栈中页面超过32
  3. 目标页获取传递过来的参数

    ts 复制代码
    params: any = router.getParams()
  4. 目标页返回上一页

    ts 复制代码
    router.back()
  5. 目标页返回指定页,并携带参数

    ts 复制代码
    router.back({
      url: 'pages/Index',
      params: { id: 10 }
    })

三、示例

  1. 代码目录结构

    |____src
    | |____main
    | | |____resources
    | | | | |____profile
    | | | | | |____main_pages.json
    | | | | |____media
    | | | | | |____back.png
    | | |____ets
    | | | |____components
    | | | | |____CommonComponents.ets
    | | | |____pages
    | | | | |____PageD.ets
    | | | | |____PageC.ets
    | | | | |____PageB.ets
    | | | | |____PageA.ets
    | | | | |____Index.ets
    
  2. main_pages.json

    json 复制代码
    {
      "src": [
        "pages/Index",
        "pages/PageA",
        "pages/PageB",
        "pages/PageC",
        "pages/PageD"
      ]
    }
  3. CommonComponents.ets

    ts 复制代码
    import router from '@ohos.router'
    
    @Component
    export struct Header {
      @State params: any = router.getParams()
    
      build() {
        Row({ space: 5 }) {
          Image($r('app.media.back'))
            .width(30)
            .onClick(() => {
              // 返回前的警告
              router.showAlertBeforeBackPage({
                message: 'Show Alert Before Back Page'
              })
              // 返回上一页
              router.back()
            })
          if (this.params) {
            Text(`Params id: ${this.params.id}`)
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
          }
        }
        .width('98%')
        .height(30)
      }
    }
  4. Index.ets

    ts 复制代码
    import router from '@ohos.router'
    
    class RouterInfo {
      // 页面路径
      url: string
      // 页面标题
      title: string
    
      constructor(url: string, title: string) {
        this.url = url
        this.title = title
      }
    }
    
    @Entry
    @Component
    struct Index {
      @State message: string = '页面列表'
      private routers: RouterInfo[] = [
        new RouterInfo('pages/PageA', 'A页面'),
        new RouterInfo('pages/PageB', 'B页面'),
        new RouterInfo('pages/PageC', 'C页面'),
        new RouterInfo('pages/PageD', 'D页面')
      ]
    
      build() {
        Column() {
          Text(this.message)
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
            .height(80)
    
          List({ space: 15 }) {
            ForEach(
              this.routers,
              (router, index) => {
                ListItem() {
                  this.RouterItem(router, index + 1)
                }
              }
            )
          }
          .layoutWeight(1)
          .alignListItem(ListItemAlign.Center)
          .width('100%')
        }
        .width('100%')
        .height('100%')
      }
    
      @Builder
      RouterItem(r: RouterInfo, i: number) {
        Row() {
          Text(i + '. ')
            .fontSize(20)
            .fontColor(Color.White)
          Text(r.title)
            .fontSize(20)
            .fontColor(Color.White)
        }
        .width('90%')
        .padding(12)
        .backgroundColor('#38F')
        .borderRadius(20)
        .shadow({ radius: 6, color: '#4F000000', offsetX: 2, offsetY: 2 })
        .onClick(() => {
          // router 跳转
          router.pushUrl(
            {
              url: r.url,
              params: { id: i }
            },
            router.RouterMode.Single,
            err => {
              if (err) {
                console.log(`路由失败,errCode: ${err.code} errMsg: ${err.message}`)
              }
            }
          )
        })
      }
    }
  5. PageA.ets

    ts 复制代码
    import { Header } from '../components/CommonComponents'
    
    @Entry
    @Component
    struct PageA {
      @State message: string = 'Page A'
    
      build() {
        Column() {
          Header()
          Row() {
            Column() {
              Text(this.message)
                .fontSize(50)
                .fontWeight(FontWeight.Bold)
            }
            .width('100%')
          }
          .height('100%')
        }.width('100%')
      }
    }
  6. 运行效果

四、总结

  1. 页面栈的最大容量上限为 32 个页面,使用 router.clear() 方法可以清空页面栈,释放内存。

  2. Router 有两种页面跳转模式,分别是:

    • router.pushUrl():目标页不会 替换当前页,而是压入页面栈,因此可以用 router.back() 返回当前页。
    • router.replaceUrl():目标页替换当前页,当前页会被销毁并释放资源,无法返回当前页。
  3. Router 有两种页面实例模式,分别是:

    • Standard:标准实例模式,每次调整都会新建一个目标并压入栈顶。默认就是这种模式。
    • Single:单实例模式,如果目标页已经在栈中,则离栈顶最近的同 url 页面会被移动到栈顶并重新加载。
  4. router 的使用步骤:

    1. 导入 router 模块
    2. 使用 router 的 API
相关推荐
Swift社区2 小时前
如何构建安全可靠的 HarmonyOS 应用
harmonyos·arkts·arkui
AI+程序员在路上2 小时前
鸿蒙系统(HarmonyOS)介绍
华为·harmonyos
lqj_本人2 小时前
鸿蒙next版开发:相机开发-录像(ArkTS)
数码相机·华为·harmonyos
23zhgjx-NanKon3 小时前
华为eNSP:MSTP
华为
bingw01144 小时前
华为机试HJ42 学英语
数据结构·算法·华为
流氓也是种气质 _Cookie4 小时前
鸿蒙HarmonyOS 网络请求获取数据Http
http·鸿蒙·鸿蒙系统
Swift社区5 小时前
HarmonyOS 如何获取设备信息(系统、版本、网络连接状态)
华为·harmonyos
Pedestrians747 小时前
OSPF总结
网络·华为
lqj_本人11 小时前
鸿蒙next版开发:相机开发-拍照(ArkTS)
数码相机·华为·harmonyos
郝晨妤11 小时前
HarmonyOS和OpenHarmony区别是什么?鸿蒙和安卓IOS的区别是什么?
android·ios·harmonyos·鸿蒙