鸿蒙开发——Router、Navigation及页面埋点

1,095 阅读5分钟

一、页面和组件

1、概念

  • 页面:即应用的UI页面。@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。

  • 自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。

2、页面生命周期

即被@Entry装饰的组件生命周期,提供以下生命周期接口:

  • onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。

  • onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。

  • onBackPress:当用户点击返回按钮时触发。

3、组件生命周期

用@Component装饰的自定义组件的生命周期

  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。

  • onDidBuild:组件build()函数执行完成之后回调该接口,不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。

  • aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量。

4、方法执行顺序

image.png

Index页面包含两个自定义组件,一个是被@Entry装饰的MyComponent,也是页面的入口组件,即页面的根节点;一个是Child,是MyComponent的子组件

  • 冷启动的初始化流程:MyComponent aboutToAppear --> MyComponent build --> MyComponent onDidBuild--> Child aboutToAppear --> Child build --> Child onDidBuild --> Index onPageShow。

  • 退出应用:Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。

  • Page1 跳转 Page2: Page2 aboutToAppear --> Page1 onPageHide --> page2 onPageShow

  • Page2返回page1:Page2 onPageHide --> Page1 onPageShow --> Page2 aboutToDisappear

二、Router

1、基本概念

页面路由指在应用程序中实现不同页面之间的跳转和数据传递,跟安卓的Router类似,相当于Activity之间的跳转。华为已经不推荐这种方式了。

1、两种跳转方式:

  • router.pushUrl:目标页面不会替换当前页,而是压入页面栈

  • router.replaceUrl:目标页面会替换当前页,并销毁当前页

2、两种实例方式:

  • Standard:多实例模式,目标页面会被添加到页面栈顶。

  • Single:单实例模式。如果目标页面的url已经存在于页面栈中,则会将离栈顶最近的同url页面移动到栈顶。如不存在,则跟实例模式一样。

3、目标页参数获取


const params: RouTmp = router.getParams() as RouTmp; // 获取传递过来的参数对象

const id: object = params.id // 获取id属性的值

const age: number = params.info.age // 获取age属性的值

4、页面返回,可携带参数返回到指定页面

router.back({
  url: 'pages/Home',
  params: {
    info: '来自Home页'
  }
});

2、生命周期监听

observer.on('routerPageUpdate', this.context, (info: RouterPageInfoParam) => {
  switch (info.state) {
    case observer.RouterPageState.ABOUT_TO_APPEAR:
      break
    case observer.RouterPageState.ABOUT_TO_DISAPPEAR:
      break
    case observer.RouterPageState.ON_PAGE_SHOW:
      break
    case observer.RouterPageState.ON_PAGE_HIDE:
      break
    case observer.RouterPageState.ON_BACK_PRESS:
      break
  }
})

三、Navigation

1、基本概念

image.png

NavDestination是Navigation子页面的根容器,用于承载子页面的一些特殊属性以及生命周期等。一般是把大首页作为Navigation,其他页面作为NavDestination使用。

虽然作为华为推荐的方式,主要是因为一镜到底、多端部署等特性依赖Navigation。其他方面暂时没发现明显的优势,思路跟安卓Jetpack中的Navigation类似,一个Activity作为根页面,其他页面作为Fragment来回切换。

注意点:

尽量不要在同一项目混用Navigation和Router,因为页面堆栈不是同一个,可能会导致页面跳转错乱。并且这两种方式跳转和传参调用方式也不同。

@Entry
@Component
struct Index {
  pathStack: NavPathStack = new NavPathStack()

  build() {
    // 设置NavPathStack并传入Navigation
    Navigation(this.pathStack) {
      // ...
    }.width('100%').height('100%')
    .title("Navigation")
    .mode(NavigationMode.Stack)
  }
}



// push page
this.pathStack.pushPath({ name: 'pageOne' })

// pop page
this.pathStack.pop()
this.pathStack.popToIndex(1)
this.pathStack.popToName('pageOne')

// replace page
this.pathStack.replacePath({ name: 'pageOne' })

// clear all page
this.pathStack.clear()

// 获取页面栈大小
let size: number = this.pathStack.size()

// 删除栈中name为PageOne的所有页面
this.pathStack.removeByName("pageOne")

// 删除指定索引的页面
this.pathStack.removeByIndexes([1,3,5])

// 获取栈中所有页面name集合
this.pathStack.getAllPathName()

// 获取索引为1的页面参数
this.pathStack.getParamByIndex(1)

// 获取PageOne页面的参数
this.pathStack.getParamByName("pageOne")

// 获取PageOne页面的索引集合
this.pathStack.getIndexByName("pageOne")
// ...

2、生命周期监听

Navigation作为路由容器,其生命周期承载在NavDestination组件上,以组件事件的形式开放。

image.png

  • aboutToAppear:在创建自定义组件后,执行其build()函数之前执行(NavDestination创建之前),允许在该方法中改变状态变量,更改将在后续执行build()函数中生效。

  • onWillAppear:NavDestination创建后,挂载到组件树之前执行,在该方法中更改状态变量会在当前帧显示生效。

  • onAppear:通用生命周期事件,NavDestination组件挂载到组件树时执行。

  • onWillShow:NavDestination组件布局显示之前执行,此时页面不可见(应用切换到前台不会触发)。

  • onShown:NavDestination组件布局显示之后执行,此时页面已完成布局。

  • onWillHide:NavDestination组件触发隐藏之前执行(应用切换到后台不会触发)。

  • onHidden:NavDestination组件触发隐藏后执行(非栈顶页面push进栈,栈顶页面pop出栈或应用切换到后台)。

  • onWillDisappear:NavDestination组件即将销毁之前执行,如果有转场动画,会在动画前触发(栈顶页面pop出栈)。

  • onDisappear:通用生命周期事件,NavDestination组件从组件树上卸载销毁时执行。

  • aboutToDisappear:自定义组件析构销毁之前执行,不允许在该方法中改变状态变量。

3、Navigation切换监听

observer.on('navDestinationSwitch', this.context, (info: observer.NavDestinationSwitchInfo) => {

})

Navigation一般只有一个,这个监听使用场景比较少

4、NavDestination切换监听

observer.on('navDestinationUpdate', (info: observer.NavDestinationInfo) => {
  switch (info.state) {
    case observer.NavDestinationState.ON_WILL_APPEAR:
      break;
    case observer.NavDestinationState.ON_SHOWN:
      break;
    case observer.NavDestinationState.ON_HIDDEN:
      break;
    case observer.NavDestinationState.ON_DISAPPEAR:
      break;
    case observer.NavDestinationState.ON_BACKPRESS:
      break;
    default:
      break;
  }
})

可以理解成页面跳转的状态监听

四、页面埋点

需要考虑多种业务场景

  • router跳转:每个Entry算是一个页面,通过routerPageUpdate监听

  • navigation:每个NavDestination算一个页面,通过navDestinationUpdate监听

  • 一个Entry页面有多个Tab,每个Tab自定义组件算一个单独的页面,就需要监听Component自定义组件的可见性,可通过onVisibleAreaChange监听。

.onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {

  if (isVisible && currentRatio >= 1.0) {
    console.info('Test Component is fully visible.')
  }

  if (!isVisible && currentRatio <= 0.0) {
    console.info('Test Component is completely invisible.')
  }
})

参考文档

@ohos.arkui.observer (无感监听)

Router切换Navigation

设置事件回调