核心差异对比

特性 LazyForEach Repeat
数据管理 需手动实现并调用IDataSource接口,开发复杂度高 由状态管理监听数据变化数组,开发简便
渲染模式 仅懒加载 全量加载 + 懒加载
状态管理 支持V1、V2 全量加载支持V1、V2,懒加载仅支持V2
模板支持 单类型组件 多类型模板(template)
节点复用 需配合@Reusable/@ReusableV2 内置复用机制(可关闭)
数据精准懒加载 不支持 支持(onLazyLoading)
增删动效 支持(需关闭复用) 支持(需和List配合使用)
组件冻结 预加载区的V1自定义组件支持冻结 复用池中的V2自定义组件支持冻结
前插保持 支持(需和List、Swiper、WaterFlow SLIDING_WINDOW模式配合使用) 支持(需和List配合使用)
支持ArrayLike类型数据源 不支持 支持
拖拽排序(onMove) 支持 支持

选择决策指导

选择LazyForEach的场景

  1. 状态管理V1项目:项目仍使用V1装饰器,无法升级到V2
  2. 精细控制需求:需要完全控制数据变化通知时机和逻辑
  3. 简单长列表:仅需基本懒加载,无复杂模板需求

选择Repeat的场景

  1. 新项目开发:使用状态管理V2,追求开发效率
  2. 复杂列表渲染:涉及多种类型的子组件,需要template区分
  3. 组件复用要求:需要便捷的节点复用能力
  4. 动态数据更新:数据频繁变化,希望自动处理
  5. 数据精准懒加载:数据源很大,需要onLazyLoading动态加载

工作原理解析

懒加载采用按需创建策略,只有当数据项进入可视区域或预加载区域时,才会创建对应的组件节点。

LazyForEach/Repeat懒加载需要和容器组件配合完成。LazyForEach/Repeat负责节点创建,容器组件负责节点布局。

Repeat还具有节点复用能力,在创建节点时,若复用池中有可用节点,则会优先复用。

整体流程

  1. 当页面首次加载、列表滑动、数据源变化时,系统会请求新的一帧,在新的一帧中重新布局页面。
  2. 容器组件根据其自身的布局算法,确定需要创建的节点,并向LazyForEach/Repeat请求这些节点。LazyForEach/Repeat接收到节点请求后,创建/复用对应的节点并返回给容器组件用于布局。
  3. 完成布局后,容器节点将当前的可视区域范围和预加载范围通知到LazyForEach/Repeat,LazyForEach/Repeat根据这些范围管理其子节点,如删除/回收滑出可视区域和预加载区域的节点等。

LazyForEach详细流程解析

LazyForEach为管理其子节点,维护两个节点池

  1. 可视节点池,管理所有可视范围内的节点
  2. 缓存节点池,管理所有预加载范围内的节点,以及临时存储待修改或待移除的节点

首次加载处理流程

可视区域节点加载

  1. 容器组件首先确定当前可视区域内的第一个节点,并从该节点开始向LazyForEach发起节点请求。LazyForEach接收到节点请求后,读取数据项,计算键值,创建对应的节点并返回给容器组件,同时将节点信息记录在可视节点池中。
  2. 容器组件每拿到一个节点,将计算其尺寸,并判断是否已填满可视区域。若已填满,则完成布局;否则继续请求下一个节点,直至填满可视区域。

预加载区域节点加载

  1. 在完成可视区域布局后,容器节点将当前的可视区域范围和预加载范围通知到LazyForEach。
  2. 在帧空闲时间中,LazyForEach针对预加载范围内的节点,读取数据项,计算键值,创建对应的节点,将节点信息记录在缓存节点池中。每帧的空闲时间有限,LazyForEach会分帧执行预加载任务。

列表滑动处理流程

  1. 列表滑动后,系统会请求新的一帧,在新的一帧中重新布局页面。
  2. 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向LazyForEach请求这一范围内的节点。
    1. LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
    2. LazyForEach读取数据项,计算键值,并在缓存节点池中查找键值相同的节点。若找到键值相同的节点,则说明节点从预加载区域滑入可视区域,直接使用该节点,并将节点从缓存节点池移动至可视节点池中;若未找到键值相同的节点,则创建新节点,并记录在可视节点池中。
  3. 容器组件拿到所有需要的节点,计算得到布局结果后,通知LazyForEach当前的可视区域范围和预加载范围。LazyForEach遍历可视节点池中的节点。若节点仍在可视范围内,则保留节点,否加将节点从可视节点池移动至缓存节点池中。
  4. 在后续的帧空闲时间中,LazyForEach同样将执行预加载任务,其流程与首次加载相同(已完成预加载的节点不会重复加载)。同时,LazyForEach将遍历缓存节点池中的节点。若节点仍在预加载范围内,则保留节点,否加将节点从缓存节点池中移除。

数据更新处理流程

  1. 开发者修改数据源,并通知LazyForEach更新,开发者必须手动调用DataChangeListener的onDataXxx()接口
  2. LazyForEach接收到通知后,将待修改的节点从可视节点池移动至缓存节点池中。同时,系统会请求新的一帧,在新的一帧中重新布局页面。
  3. 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向LazyForEach请求这一范围内的节点。
    1. LazyForEach收到一个节点请求时,首先在可视节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点未被修改,直接使用该节点。若未找到索引相同的节点,则继续到缓存节点池中查找节点。
    2. LazyForEach读取修改后的数据项,重新计算键值,并在缓存节点池中查找键值相同的节点。若找到键值相同的节点,则直接使用该节点,并将节点从缓存节点池移动至可视节点池中;若未找到键值相同的节点,则创建新节点,并记录在可视节点池中。
  4. 容器组件拿到所有需要的节点,计算得到布局结果后,通知LazyForEach当前的可视区域范围和预加载范围。LazyForEach遍历可视节点池中的节点。若节点仍在可视范围内,则保留节点,否加将节点从可视节点池移动至缓存节点池中。
  5. 在后续的帧空闲时间中,LazyForEach同样将执行预加载任务,其流程与首次加载相同(已完成预加载的节点不会重复加载)。同时,LazyForEach将遍历缓存节点池中的节点。若节点仍在预加载范围内,则保留节点,否加将节点从缓存节点池中移除。

Repeat详细流程解析(懒加载模式)

Repeat为管理其子节点,维护两个节点池

  1. 可视/预加载节点池,管理所有可视范围和预加载范围内的节点
  2. 复用节点池,管理所有已移出可视范围和预加载范围的节点,后续可被复用

首次加载处理流程

可视区域节点加载

  1. 容器组件首先确定当前可视区域内的第一个节点,并从该节点开始向Repeat发起节点请求。Repeat接收到节点请求后,读取数据项,计算键值,创建对应的节点并返回给容器组件,同时将节点信息记录在可视/预加载节点池中。
  2. 容器组件每拿到一个节点,将计算其尺寸,并判断是否已填满可视区域。若已填满,则完成布局;否则继续请求下一个节点,直至填满可视区域。

预加载区域节点加载

  1. 在完成可视区域布局后,容器节点将当前的可视区域范围和预加载范围通知到Repeat。
  2. 与LazyForEach不同,Repeat不会主动发起预加载,需要由容器组件发起预加载。
  3. 在帧空闲时间中,容器组件向Repeat请求预加载范围内的节点。Repeat接收到节点请求后,读取数据项,计算键值,创建对应的节点,将节点信息记录在可视/预加载节点池中。每帧的空闲时间有限,容器组件会分帧请求预加载节点。

列表滑动处理流程

  1. 列表滑动后,系统会请求新的一帧,在新的一帧中重新布局页面。
  2. 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向Repeat请求这一范围内的节点。
    1. Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明滑动前后该节点都在可视范围内,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
    2. Repeat读取数据项,计算模板类型、键值,并在缓复用节点池中查找模板类型和键值相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中;若未找到,则更新并复用一个模板类型相同的节点,并将节点从复用节点池移动至可视/预加载节点池中;若不存在模板类型相同的节点,则创建新节点,并记录在可视/预加载节点池中。
  3. 容器组件拿到所有需要的节点,计算得到布局结果后,通知Repeat当前的可视区域范围和预加载范围。Repeat遍历可可视/预加载节点池中的节点。若节点仍在可视范围和预加载范围内,则保留节点,否加将节点从可视/预加载节点池移动至复用节点池。
  4. 在后续的帧空闲时间中,容器组件同样会向Repeat请求预加载节点,其流程与首次加载相同。同时,Repeat会检查复用节点池中的节点数量是否超过了上限,并移除超出上限的节点。

数据更新处理流程

  1. 开发者修改数据源,状态管理自动监听变化并通知Repeat。同时,系统会请求新的一帧,在新的一帧中重新布局页面。
  2. Repeat接收到通知后,会重新组织上一帧结束时可视区域和预加载区域范围内的节点。Repeat读取范围内的数据,计算键值、模板类型,并与上一帧的结果进行比较。
    1. 对于更新前后,数据、索引、键值、模板类型均未变化的节点,直接使用。
    2. 对于键值、模板未变化的节点,更新后使用。
    3. 对于前两步中未处理的剩余节点,将节点从可视/预加载节点池移动至复用节点池。(此时不会触发下树)
    4. 对于前两步处理后,还未更新或需要创建的节点,在复用节点池中查找一个模板类型相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中(此时不会触发上树);若未找到,则当前步骤不做处理。
  3. 在新的一帧的页面布局阶段,容器组件根据其自身的布局算法,确定重新布局所涉及到的节点范围,并向Repeat请求这一范围内的节点。
    1. Repeat收到一个节点请求时,首先在可视/预加载节点池中查找索引相同的节点,若找到索引相同的节点,则说明该节点在步骤2中已完成处理,直接使用该节点。若未找到索引相同的节点,则继续到复用节点池中查找可复用的节点。
    2. Repeat读取数据项,计算模板类型、键值,并在复用节点池中查找模板类型和键值相同的节点。若找到,则更新并复用该节点,并将节点从复用节点池移动至可视/预加载节点池中;若未找到,则更新并复用一个模板类型相同的节点,并将节点从复用节点池移动至可视/预加载节点池中;若不存在模板类型相同的节点,则创建新节点,并记录在可视/预加载节点池中。
  4. 容器组件拿到所有需要的节点,计算得到布局结果后,通知Repeat当前的可视区域范围和预加载范围。Repeat遍历可可视/预加载节点池中的节点。若节点仍在可视范围和预加载范围内,则保留节点,否加将节点从可视/预加载节点池移动至复用节点池。
  5. 在后续的帧空闲时间中,容器组件同样会向Repeat请求预加载节点,其流程与首次加载相同。同时,Repeat会检查复用节点池中的节点数量是否超过了上限,并移除超出上限的节点。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐