HarmonyOS鸿蒙Next中不会走进aboutToReuse和aboutToRecycle这两个生命周期?

HarmonyOS鸿蒙Next中不会走进aboutToReuse和aboutToRecycle这两个生命周期? 外层用了repeat后,里面的自定义组件被@ComponentV2 @ReusableV2修饰,但是不会走进aboutToReuse和aboutToRecycle这两个生命周期?这是为什么?

6 回复

【背景知识】

  • Repeat根据容器组件的有效加载范围(屏幕可视区域+预加载区域)加载子组件。当容器滑动/数组改变时,Repeat会根据父容器组件的布局过程重新计算有效加载范围,并管理列表子组件节点的创建与销毁。
  • 使用@ReusableV2装饰器时,表示该自定义组件可以复用。与@Component装饰器结合使用,标记为@Reusable的自定义组件在从组件树中移除时,组件及其对应的JS对象将被放入复用缓存中。后续创建新自定义组件节点时,将复用缓存中的节点,从而节约组件重新创建的时间。

【解决方案】

Repeat组件懒加载场景中,会优先使用Repeat组件的缓存池,正常滑动场景、更新场景不涉及组件的回收与复用。当Repeat的缓存池需要扩充时将会向自定义组件要求新的子组件,此时如果复用池中有可复用的节点,将会进行复用。 在API18之后可以通过手动关闭Repeat组件自生的复用,然后使用支持V2组件的@ReusableV2,可以同时使用Repeat和aboutToReuse、aboutToRecycle。

@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({ reusable: false }) // 通过配置reusable字段选择是否启用复用功能,此处需要关闭复用功能,
          .each((obj: RepeatItem<number>) => {
            ListItem() {
              Column() {
                ReusableV2Component({ num: obj.item })
              }
            }
          })
      }.height('50%')
      .cachedCount(0)
    }
  }
}
[@ReusableV2](/user/ReusableV2)
@ComponentV2
struct ReusableV2Component {
  @Require @Param num: number;
  aboutToAppear() {
    console.info('ReusableV2Component aboutToAppear');
  }
  aboutToRecycle() {
    console.info('ReusableV2Component aboutToRecycle');
  }
  aboutToReuse() {
    console.info('ReusableV2Component aboutToReuse');
  }
  build() {
    Column() {
      Text(`${this.num}`).fontSize(50)
    }
  }
}

更多关于HarmonyOS鸿蒙Next中不会走进aboutToReuse和aboutToRecycle这两个生命周期?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


核心是解决 Repeat 自身复用逻辑@ReusableV2 组件复用机制 的冲突,实现精准复用

问题根源

Repeat 组件自身具备子节点懒加载和复用逻辑(基于可视区域自动回收 / 创建),与 [@ReusableV2](/user/ReusableV2) 的手动复用机制直接结合时,会导致复用生命周期(aboutToReuse/aboutToRecycle)未触发。

关键解决方案

  1. 关闭 Repeat 自身复用 通过配置 virtualScroll({ reusable: false })disableReuse(true),禁用 Repeat 自带的复用逻辑,交由 [@ReusableV2](/user/ReusableV2) 接管。

  2. 组件嵌套隔离 避免 [@ReusableV2](/user/ReusableV2) 组件直接作为 Repeat 的子项,需用 ** 普通 V2 组件(如 Column、Row)** 包裹隔离,示例:

    Repeat(this.data).each((item) => {
      ListItem() {
        Column() { // 中间层普通V2组件
          ReusableV2Component()
            .reuse({ reuseId: () => '唯一复用标识' })
        }
      }
    })
    
  3. 显式配置复用 ID[@ReusableV2](/user/ReusableV2) 组件上通过 .reuse({ reuseId: () => 'xxx' }) 显式声明复用标识,确保组件复用的唯一性。

通过以上步骤,可让 [@ReusableV2](/user/ReusableV2) 的复用生命周期正常触发,同时利用 Repeat 的懒加载能力,实现高效的列表组件复用。 参考:[@ReusableV2装饰器:组件复用-V2所属装饰器-状态管理(V2)-学习UI范式状态管理-UI开发 (ArkTS声明式开发范式)-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-reusablev2)

Repeat 组件自身具有子节点复用逻辑(在容器可视区域外自动回收子组件),而 @ReusableV2 的复用机制需要手动接管这一流程。两者共存时,若未正确配置,会导致复用生命周期未被触发。

直接在 Repeat 的模板中嵌入 @ReusableV2 装饰的组件可能违反复用规则。需将可复用组件包裹在普通 V2 组件内(如 Column 或 Row),以绕过 Repeat 对复用组件的直接操作。

// 关闭Repeat的默认复用

Repeat(this.data)
  .each((repeatItem: RepeatItem<string>) => {
    ListItem() {
      // 包裹一层普通V2组件
      Column() {
        ReusableV2Component() // 自定义复用组件
          .reuse({ reuseId: () => 'reuseId' }) // 显式设置复用ID
      }
    }
  })
  .disableReuse(true) // 关键:关闭Repeat自身复用
///正确嵌套组件结构
///避免将 [@ReusableV2](/user/ReusableV2) 组件直接作为 Repeat 的子项,需通过中间层组件隔离:
[@ReusableV2](/user/ReusableV2)
@ComponentV2
struct ReusableV2Component {
  @Local message: string = "Hello"
  aboutToReuse() {
    console.log('组件被复用了')
  }
  aboutToRecycle() {
    console.log('组件被回收了')
  }
  build() {
    Text(this.message)
  }
}

// 在Repeat中使用时包裹普通组件
Repeat(this.data).each((item) => {
  ListItem() {
    Column() { // 中间层普通组件
      ReusableV2Component()
        .reuse({ reuseId: () => 'itemComponent' })
    }
  }
})

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

在HarmonyOS Next中,aboutToReuseaboutToRecycle是ArkUI组件(如ListItem)的复用生命周期回调。它们仅在组件被复用时触发。如果你的组件未被复用(例如,列表数据未启用复用机制,或组件是首次创建),这两个回调就不会执行。

在HarmonyOS Next中,aboutToReuseaboutToRecycle@ReusableV2装饰器所管理的自定义组件的关键生命周期回调。根据你的描述,组件已正确使用@ComponentV2@ReusableV2修饰,但在外层使用repeat时未触发这两个回调,这通常与组件的复用条件未满足有关。

核心原因在于:@ReusableV2组件的复用并非在每次repeat渲染时都会发生,而是仅在组件节点的“结构身份”(structural identity)在更新前后保持一致时才会触发复用流程。

具体分析如下:

  1. 复用的触发条件

    • 当数据源变化导致repeat重新渲染时,框架会尝试复用之前已创建的组件实例。
    • 复用的关键是节点键(node key)的匹配。对于repeat中的项目,默认会尝试使用数据项的索引(index)或开发者通过.key()方法指定的键值来建立前后渲染中节点的对应关系。
    • 只有当两次渲染间,相同键值所对应的组件类型(即你的@ReusableV2组件)也一致时,框架才会判定该组件节点可复用,继而将旧的组件实例“移动”到新位置,并触发aboutToReuse回调。
    • 如果键值匹配失败(例如数据项顺序剧烈变动导致索引全部变化,或未提供稳定键值),框架会判定为无法复用,转而销毁旧实例并创建新实例。此时旧实例会触发aboutToDisappearaboutToRecycle(如果它之前被复用池缓存过),但新实例走的是初始化的aboutToAppear,而非aboutToReuse
  2. 你的情况排查点

    • 检查repeat的键值分配:确保为repeat的每个数据项提供了稳定且唯一的键值(例如使用数据项中的唯一ID字段),而不是依赖默认的索引。使用.key()方法显式指定。
      ForEach(
        dataArray,
        (item: MyDataModel) => {
          MyReusableComponent({ /* props */ })
        },
        (item: MyDataModel) => item.id // 提供稳定键值
      )
      
    • 确认组件类型一致性:确保repeat中渲染的始终是同一个@ReusableV2组件。
    • 观察组件实例总数:在未触发复用时,repeat数据源变化会导致自定义组件实例被不断新建和销毁。可通过日志或调试工具观察组件实例的aboutToAppearaboutToDisappear生命周期触发情况,这有助于确认是否发生了复用。
  3. aboutToRecycle的触发时机

    • 该回调在可复用组件实例被移入复用池时触发。这通常发生在组件因repeat数据源变化而不再需要(即没有对应的键值匹配其节点),但该组件类型被标记为@ReusableV2,因此实例不被立即销毁,而是放入池中等待后续可能的复用。
    • 如果组件实例从未被成功复用(即每次都是新建后直接销毁),或者销毁时复用池已满,则可能不会触发aboutToRecycle

总结:问题焦点在于repeat的节点键值管理。请优先检查是否为repeat提供了稳定的键值。若键值稳定且组件类型一致,复用生命周期应正常触发。若仍不触发,需确认数据源更新逻辑是否导致组件类型或键值生成策略发生变化。

回到顶部