[OpenHarmony] LazyForEach Repeat差异对比与流程解析
LazyForEach/Repeat懒加载需要和容器组件配合完成。LazyForEach/Repeat负责节点创建,容器组件负责节点布局。Repeat还具有节点复用能力,在创建节点时,若复用池中有可用节点,则会优先复用。策略,只有当数据项进入可视区域或预加载区域时,才会创建对应的组件节点。LazyForEach为管理其子节点,维护两个节点池。Repeat为管理其子节点,维护两个节点池。
·
核心差异对比
| 特性 | LazyForEach | Repeat |
|---|---|---|
| 数据管理 | 需手动实现并调用IDataSource接口,开发复杂度高 | 由状态管理监听数据变化数组,开发简便 |
| 渲染模式 | 仅懒加载 | 全量加载 + 懒加载 |
| 状态管理 | 支持V1、V2 | 全量加载支持V1、V2,懒加载仅支持V2 |
| 模板支持 | 单类型组件 | 多类型模板(template) |
| 节点复用 | 需配合@Reusable/@ReusableV2 | 内置复用机制(可关闭) |
| 数据精准懒加载 | 不支持 | 支持(onLazyLoading) |
| 增删动效 | 支持(需关闭复用) | 支持(需和List配合使用) |
| 组件冻结 | 预加载区的V1自定义组件支持冻结 | 复用池中的V2自定义组件支持冻结 |
| 前插保持 | 支持(需和List、Swiper、WaterFlow SLIDING_WINDOW模式配合使用) | 支持(需和List配合使用) |
| 支持ArrayLike类型数据源 | 不支持 | 支持 |
| 拖拽排序(onMove) | 支持 | 支持 |
选择决策指导
选择LazyForEach的场景
- 状态管理V1项目:项目仍使用V1装饰器,无法升级到V2
- 精细控制需求:需要完全控制数据变化通知时机和逻辑
- 简单长列表:仅需基本懒加载,无复杂模板需求
选择Repeat的场景
- 新项目开发:使用状态管理V2,追求开发效率
- 复杂列表渲染:涉及多种类型的子组件,需要template区分
- 组件复用要求:需要便捷的节点复用能力
- 动态数据更新:数据频繁变化,希望自动处理
- 数据精准懒加载:数据源很大,需要onLazyLoading动态加载
工作原理解析
懒加载采用按需创建策略,只有当数据项进入可视区域或预加载区域时,才会创建对应的组件节点。
LazyForEach/Repeat懒加载需要和容器组件配合完成。LazyForEach/Repeat负责节点创建,容器组件负责节点布局。
Repeat还具有节点复用能力,在创建节点时,若复用池中有可用节点,则会优先复用。
整体流程
- 当页面首次加载、列表滑动、数据源变化时,系统会请求新的一帧,在新的一帧中重新布局页面。
- 容器组件根据其自身的布局算法,确定需要创建的节点,并向LazyForEach/Repeat请求这些节点。LazyForEach/Repeat接收到节点请求后,创建/复用对应的节点并返回给容器组件用于布局。
- 完成布局后,容器节点将当前的可视区域范围和预加载范围通知到LazyForEach/Repeat,LazyForEach/Repeat根据这些范围管理其子节点,如删除/回收滑出可视区域和预加载区域的节点等。
LazyForEach详细流程解析
LazyForEach为管理其子节点,维护两个节点池
- 可视节点池,管理所有可视范围内的节点
- 缓存节点池,管理所有预加载范围内的节点,以及临时存储待修改或待移除的节点
首次加载处理流程
可视区域节点加载
- 容器组件首先确定当前可视区域内的第一个节点,并从该节点开始向LazyForEach发起节点请求。LazyForEach接收到节点请求后,读取数据项,计算键值,创建对应的节点并返回给容器组件,同时将节点信息记录在可视节点池中。
- 容器组件每拿到一个节点,将计算其尺寸,并判断是否已填满可视区域。若已填满,则完成布局;否则继续请求下一个节点,直至填满可视区域。
预加载区域节点加载
- 在完成可视区域布局后,容器节点将当前的可视区域范围和预加载范围通知到LazyForEach。
- 在帧空闲时间中,LazyForEach针对预加载范围内的节点,读取数据项,计算键值,创建对应的节点,将节点信息记录在缓存节点池中。每帧的空闲时间有限,LazyForEach会分帧执行预加载任务。
列表滑动处理流程
- 列表滑动后,系统会请求新的一帧,在新的一帧中重新布局页面。
- 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向LazyForEach请求这一范围内的节点。
- LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
- LazyForEach读取数据项,计算键值,并在缓存节点池中查找键值相同的节点。若找到键值相同的节点,则说明节点从预加载区域滑入可视区域,直接使用该节点,并将节点从缓存节点池移动至可视节点池中;若未找到键值相同的节点,则创建新节点,并记录在可视节点池中。
- LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
- 容器组件拿到所有需要的节点,计算得到布局结果后,通知LazyForEach当前的可视区域范围和预加载范围。LazyForEach遍历可视节点池中的节点。若节点仍在可视范围内,则保留节点,否加将节点从可视节点池移动至缓存节点池中。
- 在后续的帧空闲时间中,LazyForEach同样将执行预加载任务,其流程与首次加载相同(已完成预加载的节点不会重复加载)。同时,LazyForEach将遍历缓存节点池中的节点。若节点仍在预加载范围内,则保留节点,否加将节点从缓存节点池中移除。
数据更新处理流程
- 开发者修改数据源,并通知LazyForEach更新,开发者必须手动调用DataChangeListener的onDataXxx()接口。
- LazyForEach接收到通知后,将待修改的节点从可视节点池移动至缓存节点池中。同时,系统会请求新的一帧,在新的一帧中重新布局页面。
- 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向LazyForEach请求这一范围内的节点。
- LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点未被修改,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
- LazyForEach读取修改后的数据项,重新计算键值,并在缓存节点池中查找键值相同的节点。若找到键值相同的节点,则直接使用该节点,并将节点从缓存节点池移动至可视节点池中;若未找到键值相同的节点,则创建新节点,并记录在可视节点池中。
- LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点未被修改,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
- 容器组件拿到所有需要的节点,计算得到布局结果后,通知LazyForEach当前的可视区域范围和预加载范围。LazyForEach遍历可视节点池中的节点。若节点仍在可视范围内,则保留节点,否加将节点从可视节点池移动至缓存节点池中。
- 在后续的帧空闲时间中,LazyForEach同样将执行预加载任务,其流程与首次加载相同(已完成预加载的节点不会重复加载)。同时,LazyForEach将遍历缓存节点池中的节点。若节点仍在预加载范围内,则保留节点,否加将节点从缓存节点池中移除。
Repeat详细流程解析(懒加载模式)
Repeat为管理其子节点,维护两个节点池
- 可视/预加载节点池,管理所有可视范围和预加载范围内的节点
- 复用节点池,管理所有已移出可视范围和预加载范围的节点,后续可被复用
首次加载处理流程
可视区域节点加载
- 容器组件首先确定当前可视区域内的第一个节点,并从该节点开始向Repeat发起节点请求。Repeat接收到节点请求后,读取数据项,计算键值,创建对应的节点并返回给容器组件,同时将节点信息记录在可视/预加载节点池中。
- 容器组件每拿到一个节点,将计算其尺寸,并判断是否已填满可视区域。若已填满,则完成布局;否则继续请求下一个节点,直至填满可视区域。
预加载区域节点加载
- 在完成可视区域布局后,容器节点将当前的可视区域范围和预加载范围通知到Repeat。
- 与LazyForEach不同,Repeat不会主动发起预加载,需要由容器组件发起预加载。
- 在帧空闲时间中,容器组件向Repeat请求预加载范围内的节点。Repeat接收到节点请求后,读取数据项,计算键值,创建对应的节点,将节点信息记录在可视/预加载节点池中。每帧的空闲时间有限,容器组件会分帧请求预加载节点。
列表滑动处理流程
- 列表滑动后,系统会请求新的一帧,在新的一帧中重新布局页面。
- 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向Repeat请求这一范围内的节点。
- Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
- Repeat读取数据项,计算模板类型、键值,并在缓复用节点池中查找模板类型和键值相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中;若未找到,则更新并复用一个模板类型相同的节点,并将节点从复用节点池移动至可视/预加载节点池中;若不存在模板类型相同的节点,则创建新节点,并记录在可视/预加载节点池中。
- Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
- 容器组件拿到所有需要的节点,计算得到布局结果后,通知Repeat当前的可视区域范围和预加载范围。Repeat遍历可可视/预加载节点池中的节点。若节点仍在可视范围和预加载范围内,则保留节点,否加将节点从可视/预加载节点池移动至复用节点池。
- 在后续的帧空闲时间中,容器组件同样会向Repeat请求预加载节点,其流程与首次加载相同。同时,Repeat会检查复用节点池中的节点数量是否超过了上限,并移除超出上限的节点。
数据更新处理流程
- 开发者修改数据源,状态管理自动监听变化并通知Repeat。同时,系统会请求新的一帧,在新的一帧中重新布局页面。
- Repeat接收到通知后,会重新组织上一帧结束时可视区域和预加载区域范围内的节点。Repeat读取范围内的数据,计算键值、模板类型,并与上一帧的结果进行比较。
- 对于更新前后,数据、索引、键值、模板类型均未变化的节点,直接使用。
- 对于键值、模板未变化的节点,更新后使用。
- 对于前两步中未处理的剩余节点,将节点从可视/预加载节点池移动至复用节点池。(此时不会触发下树)
- 对于前两步处理后,还未更新或需要创建的节点,在复用节点池中查找一个模板类型相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中(此时不会触发上树);若未找到,则当前步骤不做处理。
- 对于更新前后,数据、索引、键值、模板类型均未变化的节点,直接使用。
- 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向Repeat请求这一范围内的节点。
- Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点在步骤2中已完成处理,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
- Repeat读取数据项,计算模板类型、键值,并在复用节点池中查找模板类型和键值相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中;若未找到,则更新并复用一个模板类型相同的节点,并将节点从复用节点池移动至可视/预加载节点池中;若不存在模板类型相同的节点,则创建新节点,并记录在可视/预加载节点池中。
- Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点在步骤2中已完成处理,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
- 容器组件拿到所有需要的节点,计算得到布局结果后,通知Repeat当前的可视区域范围和预加载范围。Repeat遍历可可视/预加载节点池中的节点。若节点仍在可视范围和预加载范围内,则保留节点,否加将节点从可视/预加载节点池移动至复用节点池。
- 在后续的帧空闲时间中,容器组件同样会向Repeat请求预加载节点,其流程与首次加载相同。同时,Repeat会检查复用节点池中的节点数量是否超过了上限,并移除超出上限的节点。
更多推荐


所有评论(0)