简介
默认的组件复用行为,是将子组件放在父组件的缓存池里,受到这个限制,不同父组件中的相同子组件无法复用,推荐的解决方案是将父组件改为builder函数,让子组件共享组件复用池,但是由于在一些应用场景下,父组件承载了复杂的带状态的业务逻辑,而builder是无状态的,修改会导致难以维护,因此开发者可以使用BuilderNode自行管理组件复用池。
实现思路
- 将要生成自定义组件地方用NodeContainer占位,将NodeContainer内部的NodeController按照组件类型分别存储在NodePool中。
- 每次需要创建子组件时,优先从NodePool中取出一个组件,如果NodePool中没有可复用的组件则重新创建一个,否则就更新一下数据。当NodeController销毁时,回收到NodePool中,供下次使用。
数据结构
创建和销毁过程中复用池的流程
应用场景
在应用开发中,会遇到需要页面切换的场景,比如某些视频APP的首页,就是一个List(标题)+Swiper(列表页面)实现的Tabs切换场景(如图1所示)。Swiper中每个页面都使用瀑布流加载视频列表,各个瀑布流中的子组件有可能是相同的布局,为了提升应用性能,就会有跨页面复用子组件的需求。但是在ArkUI提供的常规复用中,复用池是放在父组件中的,这就导致跨页面时无法复用上一个页面瀑布流中的子组件。此时就可以使用BuilderNode自定义一个全局的组件复用池,根据页面状态创建、回收、复用子组件,实现组件的跨页面复用。
图1 应用场景示意图
示例代码
下面通过常规复用和自定义组件复用池两种方式,对比组件复用的性能。
常规复用
-
使用List+Swiper实现Tabs页面切换。
// ... List() { ForEach(this.arrayTitle, (title: Title, index: number) => { ListItem() { TitleView({ title: title, clickListener: () => { if (title.isSelected) { return; } 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, () => { TabComp() }, (title: string) => title) } .loop(false) .onChange((index: number) => { if (this.selectIndex !== index) { this.arrayTitle[index].isSelected = true; this.arrayTitle[this.selectIndex].isSelected = false; this.selectIndex = index; } }) .cachedCount(0) // 此处设置cachedCount为0,便于性能对比,实际开发中可按需设置 // ...
-
使用Swiper组件实现轮播图,使用WaterFlow组件实现瀑布流加载数据,并给自定义组件设置reuseId,用于组件复用。