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%')
  }
}
相关推荐
枫叶丹45 小时前
【HarmonyOS之旅】HarmonyOS开发基础知识(三)
华为od·华为·华为云·harmonyos
SoraLuna10 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
AORO_BEIDOU14 小时前
单北斗+鸿蒙系统+国产芯片,遨游防爆手机自主可控“三保险”
华为·智能手机·harmonyos
博览鸿蒙15 小时前
鸿蒙操作系统(HarmonyOS)的应用开发入门
华为·harmonyos
Damon小智1 天前
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
华为·harmonyos
爱笑的眼睛112 天前
uniapp 极速上手鸿蒙开发
华为·uni-app·harmonyos
K.P2 天前
鸿蒙元服务从0到上架【第三篇】(第二招有捷径)
华为·harmonyos·鸿蒙系统
K.P2 天前
鸿蒙元服务从0到上架【第二篇】
华为·harmonyos·鸿蒙系统
敲代码的小强2 天前
Flutter项目兼容鸿蒙Next系统
flutter·华为·harmonyos
程序猿会指北2 天前
纯血鸿蒙APP实战开发——Text实现部分文本高亮和超链接样式
移动开发·harmonyos·arkts·openharmony·arkui·组件化·鸿蒙开发