HarmonyOS 应用开发之页面和自定义组件生命周期

2024-04-08 1080阅读

温馨提示:这篇文章已超过383天没有更新,请注意相关的内容是否还可用!

在开始之前,我们先明确自定义组件和页面的关系:

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

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

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

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

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

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

      组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:

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

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

        生命周期流程如下图所示,下图展示的是被@Entry装饰的组件(页面)生命周期。

        HarmonyOS 应用开发之页面和自定义组件生命周期

        根据上面的流程图,我们从自定义组件的初始创建、重新渲染和删除来详细解释。

        自定义组件的创建和渲染流程

        1. 自定义组件的创建:自定义组件的实例由ArkUI框架创建。

        2. 初始化自定义组件的成员变量:通过本地默认值或者构造方法传递参数来初始化自定义组件的成员变量,初始化顺序为成员变量的定义顺序。

        3. 如果开发者定义了aboutToAppear,则执行aboutToAppear方法。

        4. 在首次渲染的时候,执行build方法渲染系统组件,如果子组件为自定义组件,则创建自定义组件的实例。在首次渲染的过程中,框架会记录状态变量和组件的映射关系,当状态变量改变时,驱动其相关的组件刷新。

        自定义组件重新渲染

        当事件句柄被触发(比如设置了点击事件,即触发点击事件)改变了状态变量时,或者LocalStorage / AppStorage中的属性更改,并导致绑定的状态变量更改其值时:

        1. 框架观察到了变化,将启动重新渲染。

        2. 根据框架持有的两个map(自定义组件的创建和渲染流程中第4步),框架可以知道该状态变量管理了哪些UI组件,以及这些UI组件对应的更新函数。执行这些UI组件的更新函数,实现最小化更新。

        自定义组件的删除

        如果if组件的分支改变,或者ForEach循环渲染中数组的个数改变,组件将被删除:

        1. 在删除组件之前,将调用其aboutToDisappear生命周期函数,标记着该节点将要被销毁。ArkUI的节点删除机制是:后端节点直接从组件树上摘下,后端节点被销毁,对前端节点解引用,前端节点已经没有引用时,将被JS虚拟机垃圾回收。

        2. 自定义组件和它的变量将被删除,如果其有同步的变量,比如 @Link 、 @Prop 、 @StorageLink ,将从 同步源 上取消注册。

        不建议在生命周期aboutToDisappear内使用async await,如果在生命周期的aboutToDisappear使用异步操作(Promise或者回调方法),自定义组件将被保留在Promise的闭包中,直到回调方法被执行完,这个行为阻止了自定义组件的垃圾回收。

        以下示例展示了生命周期的调用时机:

        // Index.ets
        import router from '@ohos.router';
        @Entry
        @Component
        struct MyComponent {
          @State showChild: boolean = true;
          @State btnColor:string = "#FF007DFF"
          // 只有被@Entry装饰的组件才可以调用页面的生命周期
          onPageShow() {
            console.info('Index onPageShow');
          }
          // 只有被@Entry装饰的组件才可以调用页面的生命周期
          onPageHide() {
            console.info('Index onPageHide');
          }
          // 只有被@Entry装饰的组件才可以调用页面的生命周期
          onBackPress() {
            console.info('Index onBackPress');
            this.btnColor ="#FFEE0606"
            return true // 返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理
          }
          // 组件生命周期
          aboutToAppear() {
            console.info('MyComponent aboutToAppear');
          }
          // 组件生命周期
          aboutToDisappear() {
            console.info('MyComponent aboutToDisappear');
          }
          build() {
            Column() {
              // this.showChild为true,创建Child子组件,执行Child aboutToAppear
              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/page' });
                })
            }
          }
        }
        @Component
        struct Child {
          @State title: string = 'Hello World';
          // 组件生命周期
          aboutToDisappear() {
            console.info('[lifeCycle] Child aboutToDisappear')
          }
          // 组件生命周期
          aboutToAppear() {
            console.info('[lifeCycle] Child aboutToAppear')
          }
          build() {
            Text(this.title).fontSize(50).margin(20).onClick(() => {
              this.title = 'Hello ArkUI';
            })
          }
        }
        

        // page.ets
        @Entry
        @Component
        struct page {
          @State textColor: Color = Color.Black;
          @State num: number = 0
          onPageShow() {
            this.num = 5
          }
          onPageHide() {
            console.log("page onPageHide");
          }
          onBackPress() { // 不设置返回值按照false处理
            this.textColor = Color.Grey
            this.num = 0
          }
          aboutToAppear() {
            this.textColor = Color.Blue
          }
          build() {
            Column() {
              Text(`num 的值为:${this.num}`)
                .fontSize(30)
                .fontWeight(FontWeight.Bold)
                .fontColor(this.textColor)
                .margin(20)
                .onClick(() => {
                  this.num += 5
                })
            }
            .width('100%')
          }
        }
        

        以上示例中,Index页面包含两个自定义组件,一个是被@Entry装饰的MyComponent,也是页面的入口组件,即页面的根节点;一个是Child,是MyComponent的子组件。只有@Entry装饰的节点才可以使页面级别的生命周期方法生效,因此在MyComponent中声明当前Index页面的页面生命周期函数(onPageShow / onPageHide / onBackPress)。MyComponent和其子组件Child分别声明了各自的组件级别生命周期函数(aboutToAppear / aboutToDisappear)。

        • 应用冷启动的初始化流程为:MyComponent aboutToAppear --> MyComponent build --> Child aboutToAppear --> Child build --> Child build执行完毕 --> MyComponent build执行完毕 --> Index onPageShow。

        • 点击“delete Child”,if绑定的this.showChild变成false,删除Child组件,会执行Child aboutToDisappear方法。

        • 点击“push to next page”,调用router.pushUrl接口,跳转到另外一个页面,当前Index页面隐藏,执行页面生命周期Index onPageHide。此处调用的是router.pushUrl接口,Index页面被隐藏,并没有销毁,所以只调用onPageHide。跳转到新页面后,执行初始化新页面的生命周期的流程。

        • 如果调用的是router.replaceUrl,则当前Index页面被销毁,执行的生命周期流程将变为:Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。上文已经提到,组件的销毁是从组件树上直接摘下子树,所以先调用父组件的aboutToDisappear,再调用子组件的aboutToDisappear,然后执行初始化新页面的生命周期流程。

        • 点击返回按钮,触发页面生命周期Index onBackPress,且触发返回一个页面后会导致当前Index页面被销毁。

        • 最小化应用或者应用进入后台,触发Index onPageHide。当前Index页面没有被销毁,所以并不会执行组件的aboutToDisappear。应用回到前台,执行Index onPageShow。

        • 退出应用,执行Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。

          为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

          《鸿蒙开发学习手册》:

          如何快速入门:https://qr21.cn/FV7h05

          1. 基本概念
          2. 构建第一个ArkTS应用
          3. ……

          HarmonyOS 应用开发之页面和自定义组件生命周期

          开发基础知识:https://qr21.cn/FV7h05

          1. 应用基础知识
          2. 配置文件
          3. 应用数据管理
          4. 应用安全管理
          5. 应用隐私保护
          6. 三方应用调用管控机制
          7. 资源分类与访问
          8. 学习ArkTS语言
          9. ……

          HarmonyOS 应用开发之页面和自定义组件生命周期

          基于ArkTS 开发:https://qr21.cn/FV7h05

          1. Ability开发
          2. UI开发
          3. 公共事件与通知
          4. 窗口管理
          5. 媒体
          6. 安全
          7. 网络与链接
          8. 电话服务
          9. 数据管理
          10. 后台任务(Background Task)管理
          11. 设备管理
          12. 设备使用信息统计
          13. DFX
          14. 国际化开发
          15. 折叠屏系列
          16. ……

          HarmonyOS 应用开发之页面和自定义组件生命周期

          鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

          HarmonyOS 应用开发之页面和自定义组件生命周期

          鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

          1.项目开发必备面试题

          2.性能优化方向

          3.架构方向

          4.鸿蒙开发系统底层方向

          5.鸿蒙音视频开发方向

          6.鸿蒙车载开发方向

          7.鸿蒙南向开发方向

          HarmonyOS 应用开发之页面和自定义组件生命周期

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]