HarmonyOS Next鸿蒙开发:Router页面路由

212 阅读7分钟

讲路由之前先复习下堆栈的概念

栈的定义 栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素 简单定义: 栈就是一种只允许在表尾进行插入和删除操作的线性表

栈的术语说明 栈顶:允许进行插入和进行删除操作的一段成为栈顶 栈底:表的另一端称为栈底 (第一个元素进入的位置) 压栈:在栈顶位置插入元素的操作叫做压栈,或入栈、进栈 出栈:删除栈顶元素的操作叫做出栈,也叫作弹栈,或者退栈 空栈:不含元素的空表 栈溢出:当栈满的时候,如果再有元素压栈,则发生上溢,当栈空的时候,再出栈则发生下溢

image.png

理解栈 我们分别浏览了页面A、页面B、页面C,所以我们将这些页面依次压入栈,即图中打开页面部分 当用户点击后退时,我们需要退回到页面B中去,但是由于页面C在B上方,我们就必须将页面C从栈M中先弹出,放到栈N中,即图中后退部分 但是如果用户突然又想回到页面C去,原理相似的,只需要把栈N中的页面C弹出,重新压入栈M即可 而如果用户在浏览B界面的时候,打开了新的界面D,那么C就无法通过前进后退访问了,所以栈M中压入页面D的同时还需要清空栈N

image.png

鸿蒙的路由

鸿蒙系统以其独特的分布式架构和跨设备的统一体验而备受瞩目。在这个系统中,页面路由(Router)机制是连接应用各页面的关键组成部分

1.1. 两种跳转模式

Router模块提供了两种跳转模式,分别是router.pushUrl()和router.replaceUrl()。这两种模式决定了目标页是否会替换当前页。 router.pushUrl(): 目标页不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用router.back()方法返回到当前页。 router.replaceUrl(): 目标页会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。

说明 创建新页面时,请参考构建第二个页面配置第二个页面的路由。 页面栈的最大容量为32个页面。如果超过这个限制,可以调用router.clear方法清空历史页面栈,释放内存空间。

1.2. 两种实例模式

Standard:多实例模式, 也是默认情况下的跳转模式。目标页面会被添加到页面栈顶,无论栈中是否存在相同url的页面。 Single:单实例模式。 如果目标页面的url已经存在于页面栈中,则会将离栈顶最近的同url页面移动到栈顶,该页面成为新建页。如果目标页面的url在页面栈中不存在同url页面,则按照默认的多实例模式进行跳转。

1.3使用场景

场景一: 有一个主页(Home)和一个详情页(Detail),希望从主页点击一个商品,跳转到详情页。同时,需要保留主页在页面栈中,以便返回时恢复状态。这种场景下,可以使用pushUrl方法,并且使用Standard实例模式(或者省略)。

import { router } from '@kit.ArkUI';
// 在Home页面中
function onJumpClick(): void {
  router.pushUrl({
    url: 'pages/Detail' // 目标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实例模式(或者省略)。

import { router } from '@kit.ArkUI';
// 在Login页面中
function onJumpClick(): void {
  router.replaceUrl({
    url: 'pages/Profile' // 目标url
  }, 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.');
  })
}

// 登录成功,返回到首页
router.replaceUrl({
  url: 'pages/shop/ShopIndex'
})

场景三: 有一个设置页(Setting)和一个主题切换页(Theme),希望从设置页点击主题选项,跳转到主题切换页。同时,需要保证每次只有一个主题切换页存在于页面栈中,在返回时直接回到设置页。这种场景下,可以使用pushUrl方法,并且使用Single实例模式。

import { router } from '@kit.ArkUI';
// 在Setting页面中
function onJumpClick(): void {
  router.pushUrl({
    url: 'pages/Theme' // 目标url
  }, 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实例模式。

import { router } from '@kit.ArkUI';

// 在SearchResult页面中
function onJumpClick(): void {
  router.replaceUrl({
    url: 'pages/SearchDetail' // 目标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.4路由传参

在目标页面中,在需要获取参数的位置调用router.getParams方法即可,例如在onPageShow生命周期回调中:

说明 直接使用router可能导致实例不明确的问题,建议使用getUIContext获取UIContext实例,并使用getRouter获取绑定实例的router。

往页面传数据

.onClick(() => {
  router.pushUrl({
    url: 'pages/ProductDetail',
    params: {
      prodId: this.productDataModel?.prodId
    }
  })
})

接受参数

onPageShow(): void {
  let params = router.getParams() as string
  this.name = params['name']
  console.log('name '+this.name)
  console.log('name1 '+this.name1)
}
@Entry
@Component
struct Home {
  @State message: string = 'Hello World';

  onPageShow() {
    const params = this.getUIContext().getRouter().getParams() as Record<string, string>; // 获取传递过来的参数对象
    if (params) {
      const info: string = params.info as string; // 获取info属性的值
    }
  }
  ...
}

栈内存&堆内存 栈(stack) 栈stack为自动分配的内存空间,它由系统自动释放; 堆(heap) 堆heap是动态分配的内存,大小不定也不会自动释放。

JavaScript中的内存也分为栈内存和堆内存。一般来说: 栈内存中存放的是存储对象的地址; 而堆内存中存放的是存储对象的具体内容。 对于原始类型的值而言,其地址和具体内容都存在于栈内存中; 而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。 堆内存与栈内存是有区别的:栈内存运行效率比堆内存高,空间相对推内存来说较小,反之则是堆内存的特点。 所以将构造简单的原始类型值放在栈内存中,将构造复杂的引用类型值放在堆中而不影响栈的效率。

在鸿蒙中Router模块提供了两种跳转模式,分别是router.pushUrl和router.replaceUrl。这两种模式决定了目标页面是否会替换当前页。

同时,Router模块提供了两种实例模式,分别是Standard和Single。这两种模式决定了目标url是否会对应多个实例。 Standard:多实例模式,也是默认情况下的跳转模式。目标页面会被添加到页面栈顶,无论栈中是否存在相同url的页面。

Single:单实例模式。如果目标页面的url已经存在于页面栈中,则会将离栈顶最近的同url页面移动到栈顶,该页面成为新建页。如果目标页面的url在页面栈中不存在同url页面,则按照默认的多实例模式进行跳转

const params: RouTmp = router.getParams() as RouTmp;

@State name :string = (router.getParams() as string)?.['name']


接受路由参数时候,也有几种写法,以下其中一种

@Entry
@Component
struct Home {
  @State message: string = 'Hello World';

  onPageShow() {
    const params = this.getUIContext().getRouter().getParams() as Record<string, string>; // 获取传递过来的参数对象
    if (params) {
      const info: string = params.info as string; // 获取info属性的值
    }
  }
  ...
}

官网文档也有很详细的说明 developer.huawei.com/consumer/cn…