sroxck

sroxck

ArkTs Lifecycle

Before we begin, let's clarify the relationship between custom components and pages:

  • Custom Component: A UI unit decorated with @Component, which can combine multiple system components to achieve UI reuse and can invoke the component's lifecycle.
  • Page: The UI page of the application. It can consist of one or more custom components, with the custom component decorated with @Entry serving as the entry component of the page, i.e., the root node of the page. A page can have only one @Entry. Only components decorated with @Entry can invoke the page's lifecycle.

The page lifecycle, which is the lifecycle of the component decorated with @Entry, provides the following lifecycle interfaces:

  • onPageShow: Triggered each time the page is displayed, including during routing processes and when the application enters the foreground.
  • onPageHide: Triggered each time the page is hidden, including during routing processes and when the application enters the background.
  • onBackPress: Triggered when the user clicks the back button.

The component lifecycle, which generally refers to the lifecycle of custom components decorated with @Component, provides the following lifecycle interfaces:

  • aboutToAppear: This interface is called when the component is about to appear, specifically after creating a new instance of the custom component and before executing its build() function.
  • onDidBuild: This interface is called after the component's build() function has completed. It is not recommended to change state variables or use functions like animateTo in the onDidBuild function, as this may lead to unstable UI behavior.
  • aboutToDisappear: The aboutToDisappear function is executed before the custom component is destructed. It is not allowed to change state variables in the aboutToDisappear function, especially modifications to @Link variables may lead to unstable application behavior.
    Lifecycle

Creation and Rendering Process of Custom Components#

  • Creation of Custom Components: Instances of custom components are created by the ArkUI framework.
  • Initializing Member Variables of Custom Components: Member variables of the custom component are initialized by passing parameters through local default values or constructors, in the order of the definition of member variables.
  • If the developer defines aboutToAppear, the aboutToAppear method is executed.
  • During the first rendering, the build method is executed to render system components. If the child component is a custom component, an instance of the custom component is created. During the first rendering process, the framework records the mapping relationship between state variables and components, and when state variables change, it drives the related components to refresh.
  • If the developer defines onDidBuild, the onDidBuild method is executed.

Re-rendering Custom Components#

When an event handler is triggered (for example, setting a click event, which triggers a click event) that changes a state variable, or when properties in LocalStorage / AppStorage change and cause bound state variables to change their values:

  1. The framework observes the change and will initiate a re-render.
  2. Based on the two maps held by the framework (from step 4 of the creation and rendering process of custom components), the framework can know which UI components are managed by that state variable and the corresponding update functions for those UI components. It executes the update functions of these UI components to minimize updates.

Deleting Custom Components#

If the branch of an if component changes, or the number of elements in an array during a ForEach loop rendering changes, the component will be deleted:

  • Before deleting the component, its aboutToDisappear lifecycle function will be called, marking that the node is about to be destroyed. The node deletion mechanism of ArkUI is: the backend node is directly removed from the component tree, the backend node is destroyed, and the frontend node is dereferenced. When the frontend node has no references, it will be garbage collected by the JS virtual machine.
  • The custom component and its variables will be deleted. If it has synchronous variables, such as @Link, @Prop, @StorageLink, it will be unregistered from the synchronous source.

It is not recommended to use async await within the aboutToDisappear lifecycle. If asynchronous operations (Promise or callback methods) are used in the aboutToDisappear lifecycle, the custom component will be retained in the closure of the Promise until the callback method is completed, which prevents the custom component from being garbage collected.

The following example demonstrates the timing of lifecycle calls:

// Index.ets
import { router } from '@kit.ArkUI';

@Entry
@Component
struct MyComponent {
  @State showChild: boolean = true;
  @State btnColor:string = "#FF007DFF";

  // Only components decorated with @Entry can invoke the page's lifecycle
  onPageShow() {
    console.info('Index onPageShow');
  }
  // Only components decorated with @Entry can invoke the page's lifecycle
  onPageHide() {
    console.info('Index onPageHide');
  }

  // Only components decorated with @Entry can invoke the page's lifecycle
  onBackPress() {
    console.info('Index onBackPress');
    this.btnColor ="#FFEE0606";
    return true // Returning true indicates that the page handles the back logic itself, without routing; returning false indicates using the default routing back logic, treating it as false if no return value is set
  }

  // Component lifecycle
  aboutToAppear() {
    console.info('MyComponent aboutToAppear');
  }

  // Component lifecycle
  onDidBuild() {
    console.info('MyComponent onDidBuild');
  }

  // Component lifecycle
  aboutToDisappear() {
    console.info('MyComponent aboutToDisappear');
  }

  build() {
    Column() {
      // this.showChild is true, create Child subcomponent, execute Child aboutToAppear
      if (this.showChild) {
        Child()
      }
      // this.showChild is false, delete Child subcomponent, execute Child aboutToDisappear
      Button('delete Child')
      .margin(20)
      .backgroundColor(this.btnColor)
      .onClick(() => {
        this.showChild = false;
      })
      // push to page, execute onPageHide
      Button('push to next page')
        .onClick(() => {
          router.pushUrl({ url: 'pages/page' });
        })
    }

  }
}

@Component
struct Child {
  @State title: string = 'Hello World';
  // Component lifecycle
  aboutToDisappear() {
    console.info('[lifeCycle] Child aboutToDisappear')
  }

  // Component lifecycle
  onDidBuild() {
    console.info('[lifeCycle] Child onDidBuild');
  }

  // Component lifecycle
  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() { // No return value is treated as 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%')
  }
}

In the above example, the Index page contains two custom components, one is MyComponent decorated with @Entry, which is also the entry component of the page, i.e., the root node of the page; the other is Child, which is a child component of MyComponent. Only nodes decorated with @Entry can make page-level lifecycle methods effective, so the lifecycle functions of the current Index page (onPageShow / onPageHide / onBackPress) are declared in MyComponent. MyComponent and its child component Child declare their respective component-level lifecycle functions (aboutToAppear / onDidBuild / aboutToDisappear).

  • The initialization process of the application cold start is: MyComponent aboutToAppear --> MyComponent build --> MyComponent onDidBuild --> Child aboutToAppear --> Child build --> Child onDidBuild --> Index onPageShow.

  • Clicking "delete Child", if the bound this.showChild becomes false, deletes the Child component, and the Child aboutToDisappear method will be executed.

  • Clicking "push to next page" calls the router.pushUrl interface to jump to another page, the current Index page is hidden, executing the page lifecycle Index onPageHide. Here, the router.pushUrl interface is called, and the Index page is hidden but not destroyed, so only onPageHide is called. After jumping to the new page, the lifecycle initialization process of the new page is executed.

  • If router.replaceUrl is called, the current Index page is destroyed, and the lifecycle process will change to: Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear. As mentioned earlier, the destruction of components is done by directly removing the subtree from the component tree, so the parent component's aboutToDisappear is called first, followed by the child component's aboutToDisappear, and then the lifecycle initialization process of the new page is executed.

  • Clicking the back button triggers the page lifecycle Index onBackPress, and triggering the return to a page will cause the current Index page to be destroyed.

  • Minimizing the application or putting the application in the background triggers Index onPageHide. The current Index page is not destroyed, so the component's aboutToDisappear will not be executed. When the application returns to the foreground, Index onPageShow is executed.

  • Exiting the application executes Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear.

Custom Components Listening to Page Lifecycle#

Using a seamless way to listen to page routing capabilities allows custom components to listen to the page's lifecycle.

// Index.ets
import { uiObserver, router, UIObserver } from '@kit.ArkUI';

@Entry
@Component
struct Index {
  listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
    let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
    if (info.pageId == routerInfo?.pageId) {
      if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
        console.log(`Index onPageShow`);
      } else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
        console.log(`Index onPageHide`);
      }
    }
  }
  aboutToAppear(): void {
    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
    uiObserver.on('routerPageUpdate', this.listener);
  }
  aboutToDisappear(): void {
    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
    uiObserver.off('routerPageUpdate', this.listener);
  }
  build() {
    Column() {
      Text(`this page is ${this.queryRouterPageInfo()?.pageId}`)
        .fontSize(25)
      Button("push self")
        .onClick(() => {
          router.pushUrl({
            url: 'pages/Index'
          })
        })
      Column() {
        SubComponent()
      }
    }
  }
}
@Component
struct SubComponent {
  listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
    let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
    if (info.pageId == routerInfo?.pageId) {
      if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
        console.log(`SubComponent onPageShow`);
      } else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
        console.log(`SubComponent onPageHide`);
      }
    }
  }
  aboutToAppear(): void {
    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
    uiObserver.on('routerPageUpdate', this.listener);
  }
  aboutToDisappear(): void {
    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
    uiObserver.off('routerPageUpdate', this.listener);
  }
  build() {
    Column() {
      Text(`SubComponent`)
    }
  }
}

This article is synchronized and updated to xLog by Mix Space Original link: http://www.sroxck.top/posts/harmony/arkts-life

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.