HarmonyOS开发实战( Beta5版)使用reuseId标记不同组件提升复用性能实践
概述
在滑动场景下,常常会对同一类自定义组件的实例进行频繁的创建与销毁。此时可以考虑通过组件复用减少频繁创建与销毁的能耗。组件复用时,可能存在许多影响组件复用效率的操作,本篇文章将重点介绍如何通过组件复用四板斧提升复用性能。
组件复用:
- 减少组件复用的嵌套层级,如果在复用的自定义组件中再嵌套自定义组件,会存在节点构造的开销,且需要在每个嵌套的子组件中的aboutToReuse方法中实现数据的刷新,造成耗时。
- 优化状态管理,精准控制组件刷新范围,在复用的场景下,需要控制状态变量的刷新范围,避免扩大刷新范围,降低组件复用的效率。
- 复用组件嵌套结构会变更的场景,使用reuseId标记不同结构的组件构成,如:使用if else结构来控制组件的创建,会造成组件树结构的大幅变动,降低组件复用的效率。需使用reuseId标记不同的组件结构,提升复用性能。
- 不要使用函数/方法作为复用组件的入参,复用时会触发组件的构造,如果函数入参中存在耗时操作,会影响复用性能。
组件复用原理机制
复用组件嵌套结构会变更的场景,使用reuseId标记不同结构的组件构成
在自定义组件复用的场景中,如果使用if/else条件语句来控制布局的结构,会导致在不同逻辑创建不同布局结构嵌套的组件,从而造成组件树结构的不同。此时我们应该使用reuseId来区分不同结构的组件,确保系统能够根据reuseId缓存各种结构的组件,提升复用性能。正反例如下:
反例:
@Entry
@Component
struct withoutReuseId {
aboutToAppear(): void {
getFriendMomentFromRawfile();
}
build() {
Column() {
TopBar()
List({ space: ListConstants.LIST_SPACE }) {
LazyForEach(momentData, (moment: FriendMoment) => {
ListItem() {
// 此处的复用组件,只有一个reuseId,为组件的名称。但是该复用组件中又存在if else重新创建组件的逻辑
TrueOneMoment({ moment: moment, sum: this.sum, fontSize: moment.size })
}
}, (moment: FriendMoment) => moment.id)
}
.cachedCount(Constants.CACHED_COUNT)
}
}
}
@Reusable
@Component
export struct TrueOneMoment {
@Prop moment: FriendMoment;
@State sum: number = 0;
@State fontSize: number | Resource = $r('app.integer.list_history_userText_fontSize');
aboutToReuse(params: ESObject): void {
this.fontSize = params.fontSize as number;
this.sum = params.sum as number;
}
build() {
Column() {
if (this.moment.image) {
FalseOneMoment({ moment: this.moment, sum: this.sum, fontSize: this.moment.size })
} else {
OneMoment({ moment: this.moment, sum: this.sum, fontSize: this.moment.size })
}
}
.width('100%')
}
}
上述反例的操作中,在一个reuseId标识的组件TrueOneMoment中,通过if来控制其中的组件走不同的分支,选择是否创建FalseOneMoment或OneMoment组件。导致更新if分支时仍然可能走删除重创的逻辑(此处BuildItem重新创建了OneMoment组件)。考虑采用根据不同的分支设置不同的reuseId来提高复用的性能。
优化前,15号列表项复用时长为10ms左右,且存在自定义组件创建的情况。
正例:
@Entry
@Component
struct withoutReuseId {
aboutToAppear(): void {
getFriendMomentFromRawfile();
}
build() {
Column() {
TopBar()
List({ space: ListConstants.LIST_SPACE }) {
LazyForEach(momentData, (moment: FriendMoment) => {
ListItem() {
// 使用不同的reuseId标记,保证TrueOneMoment中各个子组件在复用时,不重新创建
TrueOneMoment({ moment: moment, sum: this.sum, fontSize: moment.size })
.reuseId((moment.image !=='' ?'withImage' : 'noImage'))
}
}, (moment: FriendMoment) => moment.id)
}
.cachedCount(Constants.CACHED_COUNT)
}
}
}
@Reusable
@Component
export struct TrueOneMoment {
@Prop moment: FriendMoment;
@State sum: number = 0;
@State fontSize: number | Resource = $r('app.integer.list_history_userText_fontSize');
aboutToReuse(params: ESObject): void {
this.fontSize = params.fontSize as number;
this.sum = params.sum as number;
}
build() {
Column() {
if (this.moment.image) {
FalseOneMoment({ moment: this.moment, sum: this.sum, fontSize: this.moment.size })
} else {
OneMoment({ moment: this.moment, sum: this.sum, fontSize: this.moment.size })
}
}
.width('100%')
}
}
上述正例的操作中,通过不同的reuseId来标识需要复用的组件,省去走if删除重创的逻辑,提高组件复用的效率和性能。
优化效果
针对列表滑动场景中,复用的组件中又存在多个自定义组件。通过if进行条件渲染,存在不同逻辑创建不同布局结构的组件的情况。反例中多个复用组件使用相同的复用标识reuseId,正例中采用不同的复用标识reuseId区分不同结构的自定义组件。
优化后,15号列表项复用时长缩短为3ms左右,不存在自定义组件的创建。
所以,Trace数据证明,针对不同逻辑创建不同布局结构嵌套的组件的情况,通过使用reuseId来区分不同结构的组件,能减少删除重创的逻辑,提高组件复用的效率和性能。
最后
小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。
为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:
GitCode - 全球开发者的开源社区,开源代码托管平台 希望这一份鸿蒙学习文档能够给大家带来帮助~
鸿蒙(HarmonyOS NEXT)最新学习路线
该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案
路线图适合人群:
IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术
2.视频学习教程+学习PDF文档
HarmonyOS Next 最新全套视频教程
纯血版鸿蒙全套学习文档(面试、文档、全套视频等)
总结
参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线