harmony 鸿蒙Best Practices for Component Reuse

  • 2023-10-30
  • 浏览 (473)

Best Practices for Component Reuse

Component reuse is a useful tool in the following scenarios for removing the frame rate bottleneck in the UI thread:

  1. Scrolling scenarios where instances of the same type of custom components are frequently created and destroyed
  2. Scenarios where branches of conditional rendering are switched repeatedly, and the component subtree structure in the branches is complex

For component reuse to take effect, the following conditions must be met:

  1. The custom component to reuse is decorated by the @Reusable decorator.
  2. The reusable custom component (child) created under a custom component (parent) is added to the reuse cache of the parent after being is removed from the component tree.
  3. In attempts to create a reusable child component under a custom component (parent), the child is quickly created by updating the reusable component instance of the corresponding type, if any, in the reuse cache of the parent.

Constraints

  1. @Reusable indicates that a custom component can be reused. While it can be used to decorate any custom component, take notes of the creation and update processes of the custom component to ensure that the component behaves correctly after being reused.
  2. The cache and reuse of reusable custom components can occur only under the same parent component. This means that instances of the same custom component cannot be reused under different parent components. For example, component A is a reusable component, which is also a child component of component B and in the reuse cache of component B. When component A is created in component C, component A cached in component B cannot be used in component C.
  3. The reuse of custom components improves performance mainly by reducing the time for creating JS objects of custom components and reusing the component tree structure of custom components. If you use rendering control syntax before and after the reuse of custom components, and the component tree structure of custom components is significantly changed, you will not be able to reap the benefits of performance improvement from component reuse.
  4. Component reuse occurs only when a reusable component is removed from the component tree and then added to the component tree again. For example, if ForEach is used to create a reusable custom component, component reuse cannot be triggered due to the full expansion attribute of ForEach.

Development Guidelines

  1. To maximize the component reuse performance, avoid any operations that may change the component tree structure of the custom component or re-lay out the reusable component.
  2. For achieve best possible performance, combine component reuse with the LazyForEach rendering control syntax in list scrolling scenarios.
  3. Distinguish the behavior during the creation and update of custom components. Note that the reuse of custom components is essentially a special component update behavior. The process and lifecycle seen in component creation will not occur during component reuse, and the constructor parameters of the custom component are transferred to it through the aboutToReuse lifecycle callback. In other words, the aboutToAppear lifecycle and initialization parameter input of the custom component will not occur during component reuse.
  4. Avoid time-consuming operations during the aboutToReuse lifecycle callback. The best practice is to, in aboutToReuse, only update the state variable values required for updating custom components.
  5. You do not need to update the state variables decorated by @Link, @StorageLink, @ObjectLink, and @Consume in aboutToReuse. These state variables are automatically updated, and manual update may trigger unnecessary component update.

Lifecycle

When a reusable component is removed from the component tree on the C++ side, the CustomNode instance of the custom component on the native side of the ArkUI framework is mounted to the corresponding JSView. When reuse occurs, CustomNode is referenced by JSView and the aboutToRecycle callback on ViewPU is triggered. The ViewPU instance is referenced by RecycleManager.

When a reusable component is re-added to the component tree from RecycleManager, the aboutToReuse callback on the frontend ViewPU instance is called.

Available APIs

Component lifecycle callback called before the reusable component is about to be added from the reuse cache to the component tree. The component’s state variables can be updated in this callback to display correct content. The argument type is the same as the constructor parameter type of the custom component.

aboutToReuse?(params: { [key: string]: unknown }): void;

Component lifecycle callback called when the reusable component is about to be added from the component tree to the reuse cache.

aboutToRecycle?(): void;

API used to add the reusable component to a reuse group. Components with the same reuseId value are reused in the same reuse group.

reuseId(id: string);

Reusable decorator used to declare that a component is reusable.

declare const Reusable: ClassDecorator;

Example

private dataArray: string[] = [];
  private listener: DataChangeListener;

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): any {
    return this.dataArray[index];
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
  }

  public reloadListener(): void {
    this.listener.onDataReloaded();
  }

  public registerDataChangeListener(listener: DataChangeListener): void {
    this.listener = listener;
  }

  public unregisterDataChangeListener(listener: DataChangeListener): void {
    this.listener = null;
  }
}

@Entry
@Component
struct MyComponent {
  private data: MyDataSource = new MyDataSource();

  aboutToAppear() {
    for (let i = 0; i < 1000; i++) {
      this.data.pushData(i.toString())
    }
  }

  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: string) => {
        ListItem() {
          ReusableChildComponent({ item: item })
        }
      }, item => item)
    }
    .width('100%')
    .height('100%')
  }
}

@Reusable
@Component
struct ReusableChildComponent {
  @State item: string = ''

  aboutToReuse(params) {
    this.item = params.item;
  }

  build() {
    Row() {
      Text(this.item)
        .fontSize(20)
        .margin({ left: 10 })
    }.margin({ left: 10, right: 10 })
  }
}

Samples

The following sample code from a shopping application exemplifies code before and after component reuse and the benefits that can be reaped from component reuse.

Code Before and After Component Reuse

Before Component Reuse

LazyForEach(this.GoodDataOne, (item, index) => {
  GridItem() {
    Column() {
      Image(item.img)
        .height(item.hei)
        .width('100%')
        .objectFit(ImageFit.Fill)

      Text(item.introduce)
        .fontSize(14)
        .padding({ left: 5, right: 5 })
        .margin({ top: 5 })
      Row() {
        Row() {
          Text('¥')
            .fontSize(10)
            .fontColor(Color.Red)
            .baselineOffset(-4)
          Text(item.price)
            .fontSize(16)
            .fontColor(Color.Red)
          Text(item.numb)
            .fontSize(10)
            .fontColor(Color.Gray)
            .baselineOffset(-4)
            .margin({ left: 5 })
        }

        Image($r('app.media.photo63'))
          .width(20)
          .height(10)
          .margin({ bottom: -8 })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: 5, right: 5 })
      .margin({ top: 15 })
    }
    .borderRadius(10)
    .backgroundColor(Color.White)
    .clip(true)
    .width('100%')
    .height(290)
  }
}, (item) => JSON.stringify(item))

After Component Reuse

When the component is reused, the ArkUI framework passes to the aboutToResue lifecycle callback the constructor parameters of the component. You need to assign values to the state variables to be updated in the aboutToReuse lifecycle callback, and the ArkUI framework will display the UI based on the latest state variable values.

If the structures of different instances of the same custom component vary greatly, you are advised to use reuseId to mark reuse groups for different custom component instances to achieve the optimal effect.

If a custom component has a reference to a large object or other unnecessary resources, the reference can be released in the aboutToRecycle lifecycle callback to avoid memory leak.

LazyForEach(this.GoodDataOne, (item, index) => {
  GridItem() {
    GoodItems({
      boo:item.data.boo,
      img:item.data.img,
      webimg:item.data.webimg,
      hei:item.data.hei,
      introduce:item.data.introduce,
      price:item.data.price,
      numb:item.data.numb,
      index:index
    })
    .reuseId(this.CombineStr(item.type))
  }
}, (item) => JSON.stringify(item))


@Reusable
@Component
struct GoodItems {
  @State img: Resource = $r("app.media.photo61")
  @State webimg?: string = ''
  @State hei: number = 0
  @State introduce: string = ''
  @State price: string = ''
  @State numb: string = ''
  @LocalStorageLink('storageSimpleProp') simpleVarName: string = ''
  boo: boolean = true
  index: number = 0
  controllerVideo: VideoController = new VideoController();

  aboutToReuse(params)
  {
    this.webimg = params.webimg
    this.img = params.img
    this.hei = params.hei
    this.introduce = params.introduce
    this.price = params.price
    this.numb = params.numb
  }

  build() {
    // ...
  }
}

Performance Benefit

Analysis results from the profiler tool in DevEco Studio show that, with component reuse, the average component creation time is reduced from 1800 μs to 570 μs.

before recycle

using recycle

Component Reuse Component Creation Time
Component reuse disabled 1813 μs
Component reuse enabled 570 μs

你可能感兴趣的鸿蒙文章

harmony 鸿蒙CPU Profiler

harmony 鸿蒙More Performance Improvement Methods

harmony 鸿蒙Secure and Efficient N-API Development

harmony 鸿蒙Efficient Concurrent Programming

harmony 鸿蒙Flex Layout Performance Improvement

harmony 鸿蒙TypeScript and JavaScript High-Performance Programming Practices and Tools

harmony 鸿蒙Speeding Up Application Cold Start

harmony 鸿蒙Speeding Up Application Response

harmony 鸿蒙LazyForEach Usage

harmony 鸿蒙OpenHarmony Application Performance Improvement Overview

0  赞