HarmonyOS 7 组件标了 @ReusableV2,切换父组件却还在重建?全局复用池要放对位置

页面在“列表/卡片”两种区域间来回切换,同一张复杂卡片已经写了 @ReusableV2,日志却总是出现新实例创建。问题不一定是装饰器失效:默认复用池跟着直接父组件走。父组件整体离开组件树时,它自己的池也随之消失;另一个父组件拿不到这批回收实例。API 26 新增的全局复用池正是处理跨父组件复用的工具,但它不是“打开就一定更省内存”的开关。

下面用同一张 ReusableTile 在两个面板之间切换。先看默认池,再把池放到切换期间一直存在的上层组件。

共用的可复用组件

先定义子组件,再定义使用它的父组件。这个顺序不是排版习惯:官方特别提醒,写入 poolAccepts 的组件必须在使用它的装饰器之前完成声明或导入,否则可能触发 Class '...' used before its declaration。

@ReusableV2
@ComponentV2
struct ReusableTile {
  @Param label: string = '';

  aboutToAppear(): void {
    console.info(`tile ${this.label}: appear`);
  }

  aboutToReuse(): void {
    console.info(`tile ${this.label}: reuse`);
  }

  aboutToRecycle(): void {
    console.info(`tile ${this.label}: recycle`);
  }

  build() {
    Row() {
      Text(this.label).fontSize(18)
    }
    .height(96)
    .width('100%')
    .backgroundColor('#E9F2EF')
  }
}

@ComponentV2
struct ListPanel {
  build() {
    Column() { ReusableTile({ label: '列表卡片' }) }
  }
}

@ComponentV2
struct DetailPanel {
  build() {
    Column() { ReusableTile({ label: '详情卡片' }) }
  }
}

案例一:只有子组件可复用,切换面板仍重新创建

这个页面只给 ReusableTile 加了复用装饰器,没有给持续存在的上层组件配置池。连续点击切换按钮,观察 appear、recycle 与 reuse 的日志,不要只凭肉眼觉得“切换变快了”。

@Entry
@ComponentV2
struct BaselinePage {
  @Local showList: boolean = true;

  build() {
    Column({ space: 12 }) {
      Button('切换列表/详情')
        .onClick(() => { this.showList = !this.showList; })

      if (this.showList) {
        ListPanel()
      } else {
        DetailPanel()
      }
    }
  }
}

切换时 ListPanel 与 DetailPanel 是不同的父组件实例。默认复用池归各自父组件持有;一个面板被移除后,其池不能直接供另一个面板取用。这是“写了 @ReusableV2 却看不到跨面板复用”的原因,不要把 aboutToAppear 的出现直接解释成框架从未启用复用。

案例二:在稳定的上层节点接纳这张卡片

把池放在切换时不会被销毁的 PoolOwnerPage 上,并声明它接纳 ReusableTile。这里选 perInstance:当前页面每个实例拥有自己的池,不把不同页面实例的卡片混在一起。

@Entry
@ComponentV2({
  reusePool: 'perInstance',
  poolAccepts: [ReusableTile],
  freezeWhenInactive: false
})
struct PoolOwnerPage {
  @Local showList: boolean = true;

  build() {
    Column({ space: 12 }) {
      Button('切换列表/详情')
        .onClick(() => { this.showList = !this.showList; })

      if (this.showList) {
        ListPanel()
      } else {
        DetailPanel()
      }
    }
  }
}

这段配置的重点不是“池越高越好”,而是池的拥有者要在两个子面板切换期间保持存活。框架回收或获取 ReusableTile 时会向上寻找接纳该类型的全局池,才能让不同父面板有机会共用同一批回收实例。实际日志时序会受到渲染时机影响,应看多次切换后的 reuse 记录与组件创建次数,而不是假定每次点击后立刻打印完全固定的顺序。

默认池与全局池的拥有者及生命周期示意

为什么不直接选 shared

perInstance 和 shared 解决的是不同的池拥有权:前者随单个拥有组件实例释放;后者可由同一拥有组件类的多个实例共享,只要其中还有实例存活,池可能继续存在。shared 对跨实例复用有价值,却也更需要控制缓存量。官方提供 getReusableInfo 查看池内 count,通过 maxCount 设上限;把上限调低后清理是异步的,短时间里 count 仍可能高于新上限。

观察到的现象优先检查不该先做什么
两个父面板间不复用池的拥有者是否在切换时存活继续在子卡片上叠加装饰器
编译报装饰器参数错误reusePool、poolAccepts、freezeWhenInactive 是否配齐把 poolAccepts 传空数组
编译报先使用后声明可复用组件是否定义在拥有者之前忽略编译错误继续统计性能
内存不降反升shared 池的存活范围、count/maxCount宣称复用必然节省内存

验证建议

在 API 26 工程中分别运行两页:记录同样的切换次数、appear/recycle/reuse 日志和组件创建次数;然后比较首次进入、来回切换、退出页面三个阶段。发布性能结论前,还要在目标设备上记录帧耗时和内存变化。本文的 API 限制、池查找方向和生命周期说明依据华为 2026-08-29 更新的指南;这些页面代码尚未在当前环境完成 API 26 SDK 编译和真机测量,因此这里只给出可复现步骤,不伪造提速百分比。

官方依据:全局复用:集中化的组件回收与复用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐