一、页面和组件
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、方法执行顺序
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、基本概念
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组件上,以组件事件的形式开放。
-
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.')
}
})