十二、路由、生命周期函数 原创

亚鲁鲁666
发布于 2024-12-10 22:18
浏览
0收藏

router路由

页面路由指的是在应用程序中实现不同页面之间的跳转,以及数据传递。通过 Router 模块就可以实现这个功

十二、路由、生命周期函数-鸿蒙开发者社区


2.1创建页面

之前是创建的文件,使用路由的时候需要创建页面,步骤略有不同

  1. 方法 1:直接右键新建Page(常用)
  2. 方法 2:单独添加页面并配置
2.1.1直接右键新建Page

十二、路由、生命周期函数-鸿蒙开发者社区

2.1.2单独添加页面并配置
1.新建页面

pages/DetailPage.ets

@Entry
@Component
struct DetailPage {
  build() {
    Column({ space: 15 }) {
      Text('Detail Page')
        .fontSize(40)
      Button('Back')
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

2.调整配置

:::success 小技巧:

  • 按两次 shift

:::

{
//这是一个数组,页面路径管理中心
  "src": [
    "pages/Index",
+    "pages/DetailPage"
  ]
}

:::success TIP

  • 手动新建一个页面(ets)文件,需要在**main_pages.json**中手动配置
  • 可以自动创建(会自动添加配置)
  • 删除页面**不会**自动删除配置,需要手动删除

:::

2.2.页面跳转

接下来学习路由的跳转,页面跳转是开发过程中的一个重要组成部分。

在使用应用程序时,通常需要在不同的页面之间跳转,有时还需要将数据从一个页面传递到另一个页面。接下来咱们分场景来讲解这部分的内容:

  1. 页面跳转与后退
  2. 路由模式
  3. 参数传递
2.2.1页面跳转与后退

首先来看看看使用频率最高的 跳转和 后退,核心就是使用 router 的方法

// 1. 导入
import router from '@ohos.router';

// 2.调用方法-普通跳转(可以返回)
router.pushUrl({
  url:'页面地址'
})

// 2.调用方法-替换跳转(无法返回)
router.replaceUrl({
  url:'页面地址'
})

// 2.调用方法-返回()
router.back()

:::info 试一试:

  1. 创建目录,管理页面,
  1. 在目录下添加:首页,详情页
  1. 页面 A 中分别使用pushUrl和replaceUrl跳转到页面 B
  2. 页面 B 测试 back

:::

import router from '@ohos.router'

@Entry
  @Component
  struct Index {
    build() {
      Column({ space: 15 }) {
        Text('首页')
          .fontSize(40)

        // 通过 router 模块进行跳转
        Button('去详情页-pushUrl')
          .onClick(() => {
            router.pushUrl({
              url: 'pages/day11/knowledges/router01/DetailPage',
            })
          })
        Button('去详情页-replaceUrl')
          .onClick(() => {
            router.replaceUrl({
              url: 'pages/day11/knowledges/router01/DetailPage',
            })
          })
      }
      .height('100%')
        .width('100%')
        .justifyContent(FlexAlign.Center)

    }
  }

import router from '@ohos.router'

@Entry
  @Component
  struct DetailPage {
    build() {
      Row() {
        Column() {
          Text('详情页')
            .fontSize(50)
            .fontWeight(FontWeight.Bold)
          Button('返回')
            .onClick(() => {
              router.back()
            })
        }
        .width('100%')
      }
      .height('100%')
    }
  }

:::info router.pushUrl() 和 router.replaceUrl()。都可以跳转页面,区别为是否会替换当前页。

  • router.pushUrl():目标页面不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用​​router.back()​​方法返回到当前页。
  • router.replaceUrl():目标页面会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。

划重点:pushUrl 可以返回 replaceUrl 无法返回

:::

2.2.2页面栈

页面栈是用来存储程序运行时页面的一种数据结构,遵循先进后出的原则,咱们结合刚刚的代码来说明一下:

:::info 页面栈的最大容量为32个页面

:::

2.2.2.1pushUrl的情况

先来看看 pushUrl的情况
1默认打开首页 → 首页入栈
2pushUrl 去详情页 → 详情页入栈
3back 返回上一页 → 详情页出栈
4此时页面栈中应该只有一个页面
整一个过程中,都可以 router.getLength 进行查看

十二、路由、生命周期函数-鸿蒙开发者社区

2.2.2.2replaceUrl 的情况

再来看看replaceUrl的情况
1默认打开首页 → 首页入栈
2replaceUrl 去详情页 → 详情页替换首页,首页销毁
3back 无法返回 → 没有上一页

十二、路由、生命周期函数-鸿蒙开发者社区

2.2.2.3页面栈相关 api

为了让咱们更好的获取页面栈的信息,router 模块也提供了对应的 api 以供使用

// 获取页面栈长度
router.getLength()

// 获取页面状态 
let page = router.getState();
console.log('current index = ' + page.index);
console.log('current name = ' + page.name);
console.log('current path = ' + page.path);

// 清空页面栈
router.clear()

2.3路由模式

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

:::info 简而言之:
1Standard:无论之前是否添加过,一直添加到页面栈【常用】
2Single:如果之前加过页面,会使用之前添加的页面【看情况】

:::

// 多实例模式下,router.RouterMode.Standard参数可以省略。

// pushUrl 和 replaceUrl 均可以在第二个参数设置 【路由模式】
router.pushUrl(options,mode)
router.replaceUrl(options,mode)

参数名

类型

必填

说明

options

​RouterOptions​

跳转页面描述信息。

mode

​RouterMode​

跳转页面使用的模式。

RouterMode参数说明

名称

说明

Standard

多实例模式,也是默认情况下的跳转模式。 目标页面会被添加到页面栈顶,无论栈中是否存在相同url的页面。 说明:不使用路由跳转模式时,则按照默认的多实例模式进行跳转。

Single

单实例模式。 如果目标页面的url已经存在于页面栈中,则该url页面移动到栈顶。 如果目标页面的url在页面栈中不存在同url页面,则按照默认的多实例模式进行跳转。


十二、路由、生命周期函数-鸿蒙开发者社区

:::info 试一试:
1创建目录管理多个页面:
a01.HomePage
b02.MoviePage
c03.ActorPage
2整合基础模版
3分别测试两种路由模式:

:::

@Entry
@Component
struct HomePage {
  build() {
    Stack() {
      Image($r("app.media.douban_home"))
        .width('100%')
      Button('电影详情')
        .translate({ x: -120 })
        .onClick(() => {

        })
    }
    .height('100%')
  }
}

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

  build() {
    Stack() {
      Image($r("app.media.douban_movie"))
        .width('100%')
      Button('演员')
        .translate({ x: -35, y: 180 })
        .onClick(() => {

        })
    }
    .height('100%')
  }
}

@Entry
@Component
struct ActorPage {
  build() {
    Stack() {
      Image($r("app.media.douban_actor"))
      Button('电影')
        .translate({ x: 43, y: 80 })
        .onClick(() => {
         
        })
    }
    .height('100%')
  }
}

2.3.1Strandard模式
使用 Strandard 模式,在电影和演员页面反复跳转时会持续往页面栈中添加新的页面,浪费内存

十二、路由、生命周期函数-鸿蒙开发者社区

2.3.2****Single模式
使用 Single 模式,在页面和演员页面反复跳转时会将已有的页面移到栈顶,避免浪费内存

十二、路由、生命周期函数-鸿蒙开发者社区

2.4参数

接下来看看另外一个挺常见的需求:传递参数
日常开发中有这样的场景:点击不同的电影,商品,标题。。。跳转到与之对应的详情页面。
详情页面的布局是类似的,但是内容被替换为与之对应的内容。这就是一个常见的需要传递参数的场景:
列表 → 详情

十二、路由、生命周期函数-鸿蒙开发者社区

2.4.1参数传递及接收
首先来看看如何实现页面参数传递和获取

// -----------传递参数------------
// 普通跳转 并 传递参数
router.pushUrl({
  url:'地址',
  params:{
    // 以对象的形式传递参数
  }
})
// 覆盖跳转并传递参数
router.replaceUrl(
  url:'地址',
  params:{
    // 以对象的形式传递参数
  }
)
// 返回并传递参数
router.back(
  url:'地址',
  params:{
    // 以对象的形式传递参数
  }
)

// -------------页面 B接收并解析参数------------
// aboutToAppear一会展开 (生命周期函数)
aboutToAppear(): void {
  // 1.确认内容
  console.log(JSON.stringify(router.getParams())) 
  // 2.通过 as 类型断言 转为具体的类型
  const params =  router.getParams() as 类型
  // 3. 通过点语法即可取值
  params.xxx 
}

options参数说明

名称

类型

必填

说明

url

string

表示目标页面的url,可以用以下两种格式: - 页面绝对路径,由配置文件中pages列表提供,例如: - pages/index/index - pages/detail/detail - 特殊值,如果url的值是"/",则跳转到首页。

params

object

表示路由跳转时要同时传递到目标页面的数据,切换到其他页面时,当前接收的数据失效。跳转到目标页面后,使用router.getParams()获取传递的参数

十二、路由、生命周期函数-鸿蒙开发者社区

试一试:
1创建目录管理页面
a创建首页
b创建详情页
2【首页】携带数据去 【详情页】
3【详情页】接收并解析数据

:::

export class MovieInfo {
  id: number = 0
}

@Entry
@Component
struct Home {
  build() {
    Stack() {
      Image($r('app.media.douban_home'))
        .width('100%')
      Row({ space: 10 }) {
        Button('热辣滚烫')
          .onClick(() => {
            // id: 36081094 图片 params_movie1

          })
        Button('第二十条')
          .onClick(() => {
            // id: 36208094  图片 params_movie2

          })
        Button('飞驰人生')
          .onClick(() => {
            // id: 36369452  图片 params_movie3

          })
      }
      .translate({ x: -10 })

    }
    .height('100%')
  }
}

@Entry
@Component
struct Params {
  @State imgUrl: ResourceStr = $r('app.media.params_movie1');

  aboutToAppear(): void {
      // 根据 传递过来的 id 决定渲染不同的图片
      // 热辣滚烫 // id == 36081094
      // 第二十条 // id == 36208094
      // 飞驰人生 2 // id == 36369452
  }

  build() {
    Row() {
      Image(this.imgUrl)
    }
    .height('100%')
  }
}

router路由案例-共享元素转场

当路由进行切换时,可以通过设置组件的 sharedTransition 属性将该元素标记为共享元素并设置对应的共享元素转场动效。
​​​链接​

十二、路由、生命周期函数-鸿蒙开发者社区

// 页面 A
组件(){
  
}
  .sharedTransition('标记', { duration: 500 })

// 页面 B
组件(){
  
}
  .sharedTransition('标记', { duration: 500 })

动画属性

名称

参数类型

是否必填

参数描述

duration

number

描述共享元素转场动效播放时长。 默认值:1000。 单位:毫秒。

curve

​Curve​

string

​ICurve​​ 10+

delay

number

延迟播放时间。 默认值:0。 单位:毫秒。

motionPath

​MotionPathOptions​

运动路径信息。

zIndex

number

设置Z轴。

type

​SharedTransitionEffectType​

动画类型。 默认值:SharedTransitionEffectType.Exchange。

:::info 试一试:
1基础基础模版实现,图片和文字的转场效果

:::

import router from '@ohos.router'

@Entry
@Component
struct Index {
  build() {
    Row({ space: 15 }) {
      Column({ space: 10 }) {
        Image('https://qna.smzdm.com/202404/01/660a60fac2f4c6567.jpg_e680.jpg')
          .width('100%')
          .aspectRatio(1)
        Text('小米创世版第二轮开售,一分钟秒没,我劝大家荔枝一点')
      }
      .padding(15)
      .width('50%')
      .onClick(() => {
      })
    }
    .width('100%')
  }
}

@Entry
@Component
struct DetailPage {
  build() {
    Column({ space: 15 }) {
      Column({ space: 10 }) {
        Image('https://qna.smzdm.com/202404/01/660a60fac2f4c6567.jpg_e680.jpg')
          .width('100%')
          .aspectRatio(1)
        Text('小米创世版第二轮开售,一分钟秒没,我劝大家荔枝一点')
          .fontSize(18)
      }
      .padding(15)
      .onClick(() => {
      })
    }
    .height('100%')
    .width('100%')
  }
}

页面和自定义组件的生命周期

组件和页面在创建、显示、销毁的这一整个过程中,会自动执行 一系列的【生命周期钩子】,其实就是一系列的【函数】,让开发者有机会在特定的阶段运行自己的代码
在开始之前,我们先明确自定义组件和页面的关系:
自定义组件:@Component 装饰的UI单元,
页面:即应用的UI页面。可以由一个或者多个自定义组件组成。@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。

十二、路由、生命周期函数-鸿蒙开发者社区

4.1组件-生命周期
仅仅 @Component 组件
aboutToAppear

●aboutToAppear 函数在创建自定义组件的新实例后,在执行其 build 函数之前执行。
●允许在 aboutToAppear 函数中改变状态变量,更改将在后续执行 build 函数中生效。

aboutToDisappear

●aboutToDisappear 函数在自定义组件析构销毁之前执行。
●不允许在 aboutToDisappear 函数中改变状态变量,特别是 @Link 变量的修改可能会导致应用程序行为不稳定。

@Component
struct SonCom {
  aboutToAppear(): void {
    console.log('组件创建')
  }

  aboutToDisappear(): void {
    console.log('组件销毁')
  }

  build() {
    Column() {
      Text('我是子组件')
    }
    .width(200)
    .height(200)
    .border({ width: .5 })
  }
}

@Entry
@Component
struct LiteFunc01 {
  @State show: boolean = false

  build() {
    Column() {
      Text('组件的生命周期')
      Button('切换子组件显示')
        .onClick(() => {
          this.show = !this.show
        })
      if (this.show) {
        SonCom()
      }

    }
  }
}

4.2页面-生命周期
仅页面 @Entry 组件
onPageShow

●页面每次显示时触发一次,包括路由过程、应用进入前后台等场景,仅 @Entry 修饰的自定义组件生效。

onPageHide

●页面每次隐藏时触发一次,包括路由过程、应用进入前后台等场景,仅 @Entry 修饰的自定义组件生效。

onBackPress

●当用户点击返回按钮时触发,仅 @Entry 修饰的自定义组件生效。
●内部如果返回 true,就无法通过返回键返回上一页,用户就必须和页面交互才可以返回

:::info 因为@Entry 也是@Component组件,所以页面组件同时拥有自定义组件的生命周期

:::

import router from '@ohos.router';
import promptAction from '@ohos.promptAction';
import { User } from './01.Router01';
import { BusinessError } from '@ohos.base';




@Entry
@Component
struct DetailPage {
  // aboutToAppear 会在 build 执行之前调用,后续章节展开进行讲解
  aboutToAppear() {
    const params = router.getParams() as User
    if (params) {
      promptAction.showToast({ message: params.name + '|' + params.age })
      console.log("页面初始化")
    }
  }

  onPageShow() {
    console.log("页面显示")
  }

  onPageHide() {
    console.log("页面隐藏")
  }

  onBackPress() {
    console.log("按了后退键")
    // 阻止点击返回的操作,
    return true
  }

  build() {
    Column({ space: 15 }) {
      Text('Detail Page')
        .fontSize(40)
      Button('Back')
        .onClick(() => {
           router.back()
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

Navigation路由导航容器组件(推荐)

认识Navigation组件

Navigation相比于普通的容器组件,Navigation可以实现路由跳转。一般作为首页的根容器,包括单栏、分栏和自适应三种显示模式。适用于​​一次开发,多端部署​​场景。

/pɑːθ/

Navigation的路由跳转操作都是基于页面栈NavPathStack实例提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。主要涉及页面跳转、页面返回、路由拦截等功能。

Navigation进行路由跳转的写法

具体的使用步骤是:

  1. 在根页面写法:

@Entry
@Component
struct Index {
  @State message: string = '第一个页面';
  //1. 创建一个页面栈对象并传入Navigation
  pageStack: NavPathStack = new NavPathStack()
  
  build() {
    //2. 绑定页面栈对象
    Navigation(this.pageStack) {
      Text(this.message)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .onClick(() => {
          //3.点击事件里写跳转逻辑
          this.pageStack.pushPath({ name: "minePage" })
        })
    }
    .onAppear(() => {
      this.registerInterceptors()
    })
    .height('100%')
    .width('100%')
    .title('首页') //标题
    .titleMode(NavigationTitleMode.Full) //标题样式
    .hideTitleBar(false) //标题是否隐藏
  }
}
  1. 在子页面写法:

@Builder
function mineBuilder() {
  MineComp()
}

@Component
struct MineComp {
  build() {
    NavDestination() {
      Column() {
        Text('我的')
      }
    }.title('我的页面')
  }
}
  1. 路由配置:

//路由配置在:src/main/resources/base/profile/route_map.json
{
  "routerMap": [
    {
      "name": "minePage", //子页面跳转的名字
      "pageSourceFile": "src/main/ets/pages/Navigation/mine.ets",//路由跳转的路径
      "buildFunction": "mineBuilder"//子页面的@Builder构建函数
    }
  ]
}
  1. module.json5中配置依赖

十二、路由、生命周期函数-鸿蒙开发者社区

路由拦截

路由拦截在NavPathStack实例里面的willShow页面跳转前的回调里进行阻止判断的逻辑, 和使用pop()API , 进行阻断。

//注册拦截器
registerInterceptors() {
  //设置Navigation页面跳转回调
  this.pageStack.setInterception({
    //页面跳转前的回调
    willShow: (from, to) => {
      //from从哪里来(信息)
      //to到哪里去
      //判断要去的页面是否是Navigation首页 == navbar(首页的标识)
      if (typeof to == 'string') {
        return
      }

      if (to.pathInfo.name == 'mine') {
        promptAction.showToast({
          message: JSON.stringify(to.pathInfo)
        })

        // 根据Token的值来判断用户是否登录
        // 1.有Token可以跳转成功
        // 2.没有Token不可以跳转
        const token = AppStorage.get<string>('token')
        if (!token) {
          //不让你跳, 拦截处理
          to.pathStack.pop()
          //跳转到指定页面, 如登录页面
          to.pathStack.pushPath({
            name: 'Login'
          })
          console.log('1111')
        }
        // //不让你跳, 拦截处理(回到上一页 - 回到栈顶)
        // to.pathStack.pop()
      }
    }
  })
}

子页面继续路由跳转的两种方法

1. 使用@provide、@consume状态变量实现跨层传递NavPathStack实例

十二、路由、生命周期函数-鸿蒙开发者社区

2. NavDestination 注册onReady事件:

获取上下文对象。进而获取根页面路由栈实例(所有组件页面共享同一个实例)

十二、路由、生命周期函数-鸿蒙开发者社区

还有一些属性

  • title设置标题,
  • memus设置右上角菜单,
  • mode可以设置分栏

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2024-12-12 23:15:52修改
收藏
回复
举报
回复
    相关推荐