HarmonyOS鸿蒙Next中想实现组件复用的能力
HarmonyOS鸿蒙Next中想实现组件复用的能力 【问题描述】:长列表 中使用 @Resuable 组件复用,按照官网案例写的,但是使用profiler看的时候还是提示组件复用未生效 官方文档 : https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-component-reuse#section43301824133220
官方示例代码仓库:https://gitee.com/harmonyos_samples/component-reuse/
【问题现象】:自己改写代码在附件文件中
更多关于HarmonyOS鸿蒙Next中想实现组件复用的能力的实战教程也可以访问 https://www.itying.com/category-93-b0.html
开发者你好,
组件复用是生效的,使用@ObjectLink接收aboutToReuse传递过来的参数会导致报错,需要把@ObjectLink修改为@State接收来自aboutToReuse的参数,参考列表滚动配合lazyforeach使用,修改后的MultiTypeItemPage.ets文件如下:
/*
* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ItemData } from '../model/ItemData';
import { genMockItemData } from '../common/MockData';
import { ItemDataSource } from '../model/ItemDataSource';
// [Start MultiType]
@Component
export struct MultiTypeItemPage {
// [StartExclude MultiType]
private dataSource: ItemDataSource = new ItemDataSource();
aboutToAppear(): void {
this.dataSource.pushArray(genMockItemData(1000));
}
// [EndExclude MultiType]
build() {
NavDestination() {
Column() {
List() {
LazyForEach(this.dataSource, (item: ItemData) => {
if (item.type === 0) {
TextTypeItemView({ item: item })
.reuseId('text_item_id')
} else if (item.type === 1) {
ImageTypeItemView({ item: item })
.reuseId('image_item_id')
} else if (item.type === 2) {
ThreeImageTypeItemView({ item: item })
.reuseId('three_image_item_id')
}
}, (item: ItemData) => item.id.toString())
}
// [StartExclude MultiType]
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
.cachedCount(1)
.width('100%')
.height('100%')
// [EndExclude MultiType]
}
// [StartExclude MultiType]
.width('100%')
.height('100%')
// [EndExclude MultiType]
}
// [StartExclude MultiType]
.backgroundColor(0xF1F3F5)
.title($r('app.string.index_same_list_multi_type'))
// [EndExclude MultiType]
}
}
@Reusable
@Component
struct TextTypeItemView {
// [StartExclude MultiType]
@State item: ItemData = new ItemData('1',1);
aboutToReuse(params: Record<string, Object | null | undefined>): void {
this.item = params.item as ItemData
console.log('组件复用')
}
build() {
Column() {
Text(this.item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
.maxLines(3)
.lineHeight(22)
.opacity(0.9)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%')
Row() {
Text(this.item.from)
.fontSize(12)
.fontWeight(FontWeight.Regular)
.fontColor(0x0A59F7)
Text(this.item.tail)
.fontSize(12)
.opacity(0.4)
.fontWeight(FontWeight.Regular)
.margin({ left: 6 })
.width('100%')
}
.margin({ top: 12 })
}
.padding({
top: 16,
bottom: 12,
left: 16,
right: 16
})
.margin({ top: 12, left: 16, right: 16 })
.borderRadius(12)
.backgroundColor(Color.White)
}
// [EndExclude MultiType]
}
@Reusable
@Component
struct ImageTypeItemView {
// [StartExclude MultiType]
@ObjectLink item: ItemData;
build() {
Row() {
Column() {
Text(this.item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
.maxLines(2)
.lineHeight(22)
.opacity(0.9)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(this.item.tail)
.fontSize(12)
.opacity(0.4)
.fontWeight(FontWeight.Regular)
.margin({ top: 18 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Image(this.item.preview)
.width(96)
.height(78)
.borderRadius(8)
.margin({ left: 12 })
}
.alignItems(VerticalAlign.Top)
.padding({
top: 16,
bottom: 12,
left: 16,
right: 16
})
.margin({ top: 12, left: 16, right: 16 })
.borderRadius(12)
.backgroundColor(Color.White)
}
// [EndExclude MultiType]
}
@Reusable
@Component
struct ThreeImageTypeItemView {
// [StartExclude MultiType]
@ObjectLink item: ItemData;
build() {
Column() {
Text(this.item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
.maxLines(2)
.lineHeight(22)
.opacity(0.9)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width('100%')
Row() {
Image(this.item.pics[0])
.layoutWeight(1)
.height(98)
.borderRadius({ topLeft: 8, bottomLeft: 8 })
Divider().width(2)
Image(this.item.pics[1])
.layoutWeight(1)
.height(98)
Divider().width(2)
Image(this.item.pics[2])
.layoutWeight(1)
.height(98)
.borderRadius({ topRight: 8, bottomRight: 8 })
}
.margin({ top: 8 })
Text(this.item.tail)
.fontSize(12)
.opacity(0.4)
.fontWeight(FontWeight.Regular)
.margin({ top: 18 })
.margin({ top: 8 })
}
.alignItems(HorizontalAlign.Start)
.padding({
top: 16,
bottom: 12,
left: 16,
right: 16
})
.margin({ top: 12, left: 16, right: 16 })
.borderRadius(12)
.backgroundColor(Color.White)
}
// [EndExclude MultiType]
}
// [End MultiType]
更多关于HarmonyOS鸿蒙Next中想实现组件复用的能力的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
从提供的代码上看,可以从以下方面进行检查调整
在 LazyForEach 中使用 @Reusable 组件时必须通过 .reuseId() 方法显式设置唯一标识符。 代码问题:当前示例中 reuseId 仅作为注释存在(//TODO reuseId),实际未添加 .reuseId() 方法调用。 复用组件时需通过 aboutToReuse 方法更新数据,但若参数传递不完整或类型不匹配,会导致复用失败。 增加 cachedCount 提升复用机会
HarmonyOS Next中组件复用主要通过ArkUI的@Builder、@BuilderParam和@Extend装饰器实现。@Builder用于定义可复用的UI片段;@BuilderParam允许组件接收@Builder作为参数,实现动态UI组合;@Extend用于扩展组件样式,实现样式复用。此外,自定义组件是核心复用单元,通过封装状态、样式和逻辑来构建独立功能模块。
在HarmonyOS Next中,@Reusable 装饰器用于标记组件具备可复用能力,但实际复用效果需要满足特定条件。根据你的描述,Profiler提示复用未生效,通常有以下原因:
-
组件状态未满足复用条件:
@Reusable组件仅在组件树结构相同且组件状态(状态变量、属性)一致时才会被复用。如果每次渲染时状态或属性发生变化,复用将不会触发。 -
父组件未使用复用优化:确保父组件(如长列表的
ListItem)使用了if/else或ForEach等动态渲染逻辑,且子组件被正确标记为@Reusable。 -
组件结构不一致:检查复用的组件是否在每次渲染时保持相同的组件结构(如相同的子组件数量和类型)。
建议检查:
- 确保
@Reusable组件内部状态稳定,避免每次渲染都生成新状态。 - 使用 Profiler 工具观察组件更新时的差异,确认是否有属性变化导致复用失效。
- 参考官方示例,对比代码中
@Reusable组件的使用方式是否一致。

