📚鸿蒙开发往期学习笔录✒️:
✒️ 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
✒️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✒️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✒️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✒️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✒️ 记录一场鸿蒙开发岗位面试经历~
✒️ 持续更新中……
简介
默认的组件复用行为,是将子组件放在父组件的缓存池里,受到这个限制,不同父组件中的相同子组件无法复用,推荐的解决方案是将父组件改为builder函数,让子组件共享组件复用池,但是由于在一些应用场景下,父组件承载了复杂的带状态的业务逻辑,而builder是无状态的,修改会导致难以维护,因此开发者可以使用BuilderNode自行管理组件复用池。
实现思路
- 将要生成自定义组件地方用 NodeContainer 占位,将NodeContainer内部的 NodeController 按照组件类型分别存储在NodePool中。
- 每次创建子组件时,优先通过NodePool的getNode方法尝试复用已存在的NodeController组件,若无可复用组件则调用makeNode方法新建;若复用成功,则调用update方法更新组件数据。
- 当NodeController销毁时,NodeItem回收到NodePool中,供下次使用。
组件复用原理
在ArkUI中,当页面退出时,系统默认会销毁页面上的所有节点及其对应的NodeItem实例,以释放资源。为了提升性能和资源利用率,应用侧可以主动保存NodeItem实例。通过这种方法,能够有效延长这些NodeItem实例的生命周期,避免不必要的重建开销,从而在后续页面或组件的创建过程中实现快速复用。
下图为复用池中NodeItem实例跟随NodeContainer组件创建与销毁的复用过程。
数据结构
NodeItem继承NodeController,并实现makeNode方法,创建组件。NodePool通过HashMap管理NodeItem的复用和回收。
应用场景
在应用开发中,会遇到需要页面切换的场景,比如某些视频APP的首页,就是一个List(标题)+Swiper(列表页面)实现的Tabs切换场景。Swiper中每个页面都使用瀑布流加载视频列表,各个瀑布流中的子组件有可能是相同的布局,为了提升应用性能,就会有跨页面复用子组件的需求。但是在ArkUI提供的常规复用中,复用池是放在父组件中的,这就导致跨页面时无法复用上一个页面瀑布流中的子组件。此时就可以使用 BuilderNode 自定义一个全局的组件复用池,根据页面状态创建、回收、复用子组件,实现组件的跨页面复用。
组件复用性能对比
下面通过常规复用和自定义组件复用池两种方式,对比组件复用的性能。
常规复用
- 使用List+Swiper实现Tabs页面切换。
List() {
ForEach(this.arrayTitle, (title: Title, index: number) => {
ListItem() {
TitleView({
title: title, clickListener: () => {
if (title.isSelected) {
return;
}
// 点击标题时,Swiper组件跳转到对应的页面
this.swiperController.changeIndex(index, true);
// 设置标题为选中状态
this.arrayTitle[index].isSelected = true;
this.arrayTitle[this.selectIndex].isSelected = false;
this.selectIndex = index;
}
})
// ...
}
})
}
.height(30)
.listDirection(Axis.Horizontal)
Swiper(this.swiperController) {
// 使用LazyForEach,使Swiper页面按需加载,而不是一次全部创建
LazyForEach(this.array, (item: string, index: number) => {
// 常规复用代码,可按需注释运行
// TabComp({ index: index })
// 自定义组件复用池代码
TabNode({ index: index })
}, (title: string) => title)
}
.loop(false)
.onChange((index: number) => {
// Swiper滑动切换页面时,改变标题栏的选中状态
if (this.selectIndex !== index) {
this.arrayTitle[index].isSelected = true;
this.arrayTitle[this.selectIndex].isSelected = false;
this.selectIndex = index;
}
})
.cachedCount(0) // 此处设置cachedCount为0,便于性能对比,实际开发中可按需设置
- 使用Swiper组件