页面和组件

首先需要了解自定义组件和页面的区别

  • 自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。
  • 页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。
    harmony提供的生命周期分为页面的生命周期和组件的生命周期。
    我们知道页面分为可见和不可见那么他的生命周期就有
  • onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
  • onPageHinde:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
  • onBackPress:当用户点击返回按钮时触发。
    组件的展示依赖于页面,那么就有出现和销毁,他的生命周期有
  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
  • onDidBuild:- 组件build()函数执行完成之后回调该接口,不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。
  • aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。
    他们之间的关系是:
    image.png

自定义组件渲染流程

  1. 自定义组件的实例由ArkUI框架创建
  2. 初始化自定义组件的成员变量:通过本地默认值或者构造方法传递参数来初始化自定义组件的成员变量,初始化顺序为成员变量的定义顺序。
  3. 执行aboutToAppear 方法
  4. 首次渲染的时候,执行build方法渲染系统组件,如果子组件为自定义组件,则创建自定义组件的实例。在首次渲染的过程中,框架会记录状态变量和组件的映射关系,当状态变量改变时,驱动其相关的组件刷新。
  5. 执行onDidBuild方法

自定义组件重新渲染

当状态变量改变时导致绑定的状态变量更改会发生一下流程

  1. 框架观察到了变化,将启动重新渲染。
  2. 根据框架持有的两个map(状态变量和组件的映射关系),框架可以知道该状态变量管理了哪些UI组件,以及这些UI组件对应的更新函数。执行这些UI组件的更新函数,实现最小化更新。

自定义组件的删除

  1. 在删除组件之前,将调用其aboutToDisappear生命周期函数,标记着该节点将要被销毁。ArkUI的节点删除机制是:后端节点直接从组件树上摘下,后端节点被销毁,对前端节点解引用,前端节点已经没有引用时,将被JS虚拟机垃圾回收。
    1. 自定义组件和它的变量将被删除,如果其有同步的变量,比如@Link、@Prop、@StorageLink,将从同步源上取消注册。
      不建议在生命周期aboutToDisappear内使用async await,如果在生命周期的aboutToDisappear使用异步操作(Promise或者回调方法),自定义组件将被保留在Promise的闭包中,直到回调方法被执行完,这个行为阻止了自定义组件的垃圾回收。

实例演示

创建一个项目并修改 Index 的page

import { router } from '@kit.ArkUI';  
import { BusinessError } from '@kit.BasicServicesKit';  
  
@Entry  
@Component  
struct Index {  
  @State showChild: boolean = true;  
  @State btnColor: string = "#FF007DFF";  
  
  onPageShow(): void {  
    console.info('Index onPageShow');  
  }  
  
  onPageHide(): void {  
    console.info('Index onPageHide');  
  }  
  
  onBackPress() {  
    console.info('Index onBackPress');  
    this.btnColor = "#FFEE0606";  
    return true // 返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理  
  }  
  
  aboutToAppear(): void {  
    console.info('Index 组件 aboutToAppear');  
  }  
  
  onDidBuild(): void {  
    console.info('Index 组件 onDidBuild');  
  }  
  
  aboutToDisappear(): void {  
    console.info('Index 组件 aboutToDisappear');  
  }  
  
  build() {  
    Column() {  
      if (this.showChild) {  
        Child()  
      }  
      // this.showChild为false,删除Child子组件,执行Child aboutToDisappear  
      Button('delete Child')  
        .margin(20)  
        .backgroundColor(this.btnColor)  
        .onClick(() => {  
          this.showChild = false;  
        })  
      // push到page页面,执行onPageHide  
      Button('push to next page')  
        .onClick(() => {  
          //跳转第二个页面  
          router.pushUrl({ url: 'pages/Second' }).then(() => {  
            console.info('Succeeded in jumping to the second page.')  
          }).catch((err: BusinessError) => {  
            console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)  
          })  
        })  
    }  
    .height('100%')  
    .width('100%')  
  }  
}  
  
  
@Component  
struct Child {  
  @State title: string = 'Hello World';  
  
  // 组件生命周期  
  aboutToDisappear() {  
    console.info('[lifeCycle] Child aboutToDisappear');  
  }  
  
  // 组件生命周期  
  onDidBuild() {  
    console.info('[lifeCycle] Child onDidBuild');  
  }  
  
  // 组件生命周期  
  aboutToAppear() {  
    console.info('[lifeCycle] Child aboutToAppear');  
  }  
  
  build() {  
    Text(this.title)  
      .fontSize(50)  
      .margin(20)  
      .onClick(() => {  
        this.title = 'Hello ArkUI';  
      })  
  }  
}
  1. 运行会看见以下log
    61218d89-40cc-4613-895a-6d98e6e9a174.jpeg
    所以应用冷启动的初始化流程为:页面顶层组件的aboutToAppear -> 页面顶层组件的build -> 页面顶层组件的onDidBuild -> 子组件的aboutToAppear -> 子组件的onDidBuild -> 页面的onPageShow

  2. 当移除子组件时log
    5edfcc3c-92e5-4aca-a257-27d1b64f55db.jpeg
    所以会执行子组件的 aboutToDisappear方法,

  3. 当跳转到其他页面时,执行router.pushUrl当前页面不会销毁,此时的 log
    d1119777-d57b-4091-9c60-db01edab77a2.jpeg
    第二个页面的 aboutToAppear -> 第二个页面的 build -> 第二个页面的 onDidBuild -> 第二个页面的子组件的 aboutToAppear -> 第二个页面的子组件的 onDidBuild -> 第一个页面的 onPageHide -> 第二个页面的 onPageShow

  4. 当跳转到其他页面时,执行router.replaceUrl当前页面会销毁,此时的 log
    5897fda2-e9d5-4494-9dc6-d7c88039b18f.jpeg
    第二个页面的 aboutToAppear -> 第二个页面的 build -> 第二个页面的 onDidBuild -> 第二个页面的子组件的 aboutToAppear -> 第二个页面的子组件的 onDidBuild -> 第一个页面的 onPageHide -> 第二个页面的 onPageShow -> 第一个页面组件的 aboutToDisappear -> 第一个页面子组件的 aboutToDisappear, 组件的销毁是从组件树上直接摘下子树,所以先调用父组件的aboutToDisappear,再调用子组件的aboutToDisappear。

  5. 从第二个页面返回时的log
    a3e1875a-13b5-4891-b2d1-94b0288042d7.jpeg
    第二个页面的 onPageHide -> 第一个页面的onPageShow -> 第二个页面组件的aboutToDisappear -> 第二个页面子组件的 aboutToDisappear




©超超使用Stellar 1.29.1 创建。

发表了 14 篇文章 · 总计 21.5k 字