HarmonyOS 组件复用 @ReusableV2 装饰器的基本使用 2

2025-06-26 11:55:39
108次阅读
0个评论

HarmonyOS 组件复用 @ReusableV2 装饰器的基本使用 2

前言

接上文 HarmonyOS 组件复用 @ReusableV2 装饰器的基本使用

上文已经介绍过了 @ReusableV2 的基本使用和生命周期,本文主要讲解**@ReusableV2**的使用场景

使用场景

如官网中所总结的,因为以下场景都经常伴随着组件的显示和隐藏,@ReusableV2 常见的使用场景如下:

  1. 搭配 if 使用
  2. 搭配 Repeat 使用
  3. 搭配 ForEach 使用
  4. 搭配 LazyForEach 使用

搭配 if 使用

@Entry
@ComponentV2
struct Index {
  @Local condition: boolean = true;

  build() {
    Column() {
      Button('回收/复用').onClick(() => {
        this.condition = !this.condition;
      }) // 点击切换回收/复用状态
      if (this.condition) {
        ReusableV2Component()
      }
    }
  }
}

@ReusableV2
@ComponentV2
struct ReusableV2Component {
  @Local message: string = 'Hello World';

  aboutToRecycle() {
    console.log('ReusableV2Component aboutToRecycle'); // 回收时被调用
  }

  aboutToReuse() {
    console.log('ReusableV2Component aboutToReuse'); // 复用时被调用
  }

  build() {
    Column() {
      Text(this.message)
    }
  }
}

效果

PixPin_2025-06-26_08-48-58

搭配 Repeat each 使用 懒加载场景

Repeat 组件懒加载场景中,将会优先使用 Repeat 组件的缓存池,正常滑动场景、更新场景不涉及组件的回收与复用。当 Repeat 的缓存池需要扩充时将会向自定义组件要求新的子组件,此时如果复用池中有可复用的节点,将会进行复用。

@Entry
@ComponentV2
struct Index {
  @Local condition: boolean = true;
  @Local simpleList: number[] = [];

  aboutToAppear(): void {
    for (let i = 0; i < 100; i++) {
      this.simpleList.push(i)
    }
  }

  build() {
    Column() {
      Button('改变condition').onClick(() => {
        this.condition = !this.condition;
      })
      if (this.condition) {
        // 此处仅做演示使用,让复用池中填充3个组件
        ReusableV2Component({ num: 0 })
        ReusableV2Component({ num: 0 })
        ReusableV2Component({ num: 0 })
      }
      List({ space: 10 }) {
        Repeat(this.simpleList)
          .virtualScroll()
          .each((obj: RepeatItem<number>) => {
            ListItem() {
              Column() {
                ReusableV2Component({ num: obj.item })
              }
            }
          })
      }.height('50%')
      .cachedCount(2)
    }
  }
}

@ReusableV2
@ComponentV2
struct ReusableV2Component {
  @Require @Param num: number;

  aboutToAppear() {
    console.log('ReusableV2Component aboutToAppear');
  }

  aboutToRecycle() {
    console.log('ReusableV2Component aboutToRecycle');
  }

  aboutToReuse() {
    console.log('ReusableV2Component aboutToReuse');
  }

  build() {
    Column() {
      Text(`${this.num}`).fontSize(50)
    }
  }
}

效果

PixPin_2025-06-26_10-08-37

步骤解释:

  1. 正常滑动列表,不会触发组件的 回收和复用
  2. 点击按钮,条件渲染 组件时,就会触发组件的回收和复用了

virtualScroll

virtualScroll 表示 虚拟列表,使用不使用 virtualScroll 的区别在于

使用 virtualScroll 不使用 virtualScroll
预加载区的 ListItem 会在系统空闲时创建和布局 预加载区内的 ListItem 会在首帧创建,系统空闲时布局 ListItem 和创建 ListItem 内部的子结构

使用 virtualScroll :

image-20250626090210427

不使用 virtualScroll :

image-20250626090220294

搭配 Repeat each 使用 非懒加载场景

Repeat 组件非懒加载场景中,会在删除/创建子树时触发回收/复用。

@Entry
@ComponentV2
struct Index {
  @Local simpleList: number[] = [1, 2, 3, 4, 5];
  @Local condition: boolean = true;

  build() {
    Column() {
      Button('删除/创建Repeat').onClick(() => {
        this.condition = !this.condition;
      })
      Button('增加元素').onClick(() => {
        this.simpleList.push(this.simpleList.length + 1);
      })
      Button('删除元素').onClick(() => {
        this.simpleList.pop();
      })
      Button('更改元素').onClick(() => {
        this.simpleList[0]++;
      })
      if (this.condition) {
        List({ space: 10 }) {
          Repeat(this.simpleList)
            .each((obj: RepeatItem<number>) => {
              ListItem() {
                Column() {
                  ReusableV2Component({ num: obj.item })
                }
              }
            })
        }
      }
    }
  }
}

@ReusableV2
@ComponentV2
struct ReusableV2Component {
  @Require @Param num: number;

  aboutToAppear() {
    console.log('ReusableV2Component aboutToAppear');
  }

  aboutToRecycle() {
    console.log('ReusableV2Component aboutToRecycle');
  }

  aboutToReuse() {
    console.log('ReusableV2Component aboutToReuse');
  }

  build() {
    Column() {
      Text(`${this.num}`)
    }
  }
}

image-20250626111932624

操作步骤如下:

  1. 运行项目

    触发 aboutToAppear
    
  2. 增加元素

    触发 aboutToAppear
    
  3. 删除元素

    触发 aboutToRecycle
    
  4. 更改元素

    无输出
    
  5. 删除/创建 Repeat

    触发 aboutToRecycle 和 aboutToReuse
    

搭配 Repeat 和 ForEach

推荐开发者使用 Repeat 组件的非懒加载场景代替 ForEach 组件

@Entry
@ComponentV2
struct Index {
  @Local simpleList: number[] = [0, 1, 2, 3, 4, 5];
  build() {
    Column() {
      ForEach(this.simpleList, (num: number, index) => {
        Row() {
          Button('点击修改').onClick(()=>{this.simpleList[index]++;})
          ReusableV2Component({ num: num })
        }
      }) // 每次修改完key发生变化
    }
  }
}
@ReusableV2
@ComponentV2
struct ReusableV2Component {
  @Require @Param num: number;
  aboutToAppear() {
    console.log('ReusableV2Component aboutToAppear', this.num); // 创建时触发
  }
  aboutToRecycle() {
    console.log('ReusableV2Component aboutToRecycle', this.num); // 回收时触发
  }
  aboutToReuse() {
    console.log('ReusableV2Component aboutToReuse', this.num); // 复用时触发
  }
  build() {
    Column() {
      Text(`child: ${this.num}`)
    }
  }
}

搭配 Repeat 和 LazyForEach

推荐开发者使用 Repeat 组件的懒加载场景代替 LazyForEach 组件

class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: StringData[] = [];


  public totalCount(): number {
    return 0;
  }


  public getData(index: number): StringData {
    return this.originDataArray[index];
  }


  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }


  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }


  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }


  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }


  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }


  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }


  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }


  notifyDatasetChange(operations: DataOperation[]): void {
    this.listeners.forEach(listener => {
      listener.onDatasetChange(operations);
    })
  }
}


class MyDataSource extends BasicDataSource {
  private dataArray: StringData[] = [];


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


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


  public addData(index: number, data: StringData): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }


  public pushData(data: StringData): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}


@ObservedV2
class StringData {
  @Trace message: string;
  constructor(message: string) {
    this.message = message;
  }
}


@Entry
@ComponentV2
struct Index {
  data: MyDataSource = new MyDataSource(); // 数据源


  aboutToAppear() {
    for (let i = 0; i <= 200; i++) {
      this.data.pushData(new StringData('Hello' + i));
    }
  }
  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: StringData, index: number) => {
        ListItem() {
          Column() {
            Text(item.message)
            ChildComponent({ data: item.message })
              .onClick(() => {
                item.message += '!'; // message为@Trace装饰的变量,可观察变化
              })
          }
        }
      })
    }.cachedCount(5)
  }
}


@ReusableV2
@ComponentV2
struct ChildComponent {
  @Param @Require data: string;
  aboutToAppear(): void {
    console.log('ChildComponent aboutToAppear', this.data);
  }
  aboutToDisappear(): void {
    console.log('ChildComponent aboutToDisappear', this.data);
  }
  aboutToReuse(): void {
    console.log('ChildComponent aboutToReuse', this.data); // 复用时触发
  }
  aboutToRecycle(): void {
    console.log('ChildComponent aboutToRecycle', this.data); // 回收时触发
  }
  build() {
    Row() {
      Text(this.data).fontSize(50)
    }
  }
}

关于我们

关于青蓝逐码组织

如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯甚至你想要做出一款属于自己的应用!欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

image-20250622200325374

收藏00

登录 后评论。没有帐号? 注册 一个。