鸿蒙Next数据懒加载LazyForEach用法总结

server/2024/12/19 3:15:54/

在鸿蒙Next开发中,LazyForEach提供了高效的数据懒加载机制,适用于处理大量数据的列表展示等场景,可有效提升性能和内存管理。以下是其详细用法总结。

一、使用限制

  1. 容器组件要求:必须在特定容器组件(List、Grid、Swiper、WaterFlow)内使用,且这些组件支持配置cachedCount属性实现按需加载。
  2. 数量限制:容器组件内只能包含一个LazyForEach。
  3. 子组件规则:每次迭代必须且只能创建一个子组件,且生成的子组件必须符合父容器组件对子组件的要求。
  4. 条件渲染支持:允许包含在if/else条件渲染语句中,也可在其内部出现if/else条件渲染语句。
  5. 键值唯一性:键值生成器必须为每个数据生成唯一值,否则会导致UI组件渲染问题。
  6. 更新方式限制:必须使用DataChangeListener对象更新,对第一个参数dataSource重新赋值会异常,且dataSource使用状态变量时,状态变量改变不会触发UI刷新。为高性能渲染,需通过onDataChange方法更新UI并生成不同键值触发组件刷新。
  7. 装饰器要求:必须和@Reusable装饰器一起使用才能触发节点复用,需将@Reusable装饰在LazyForEach列表的组件上。

二、键值生成规则

  1. 系统默认规则:若开发者未定义keyGenerator函数,ArkUI框架使用默认函数(item: Object, index: number) => { return viewId + '-' + index.toString(); }(viewId在编译器转换过程中生成,同一个LazyForEach组件内其viewId一致)。
  2. 自定义规则:开发者可通过提供keyGenerator函数来自定义键值生成逻辑。

三、组件创建规则

1. 首次渲染

  • 生成不同键值:根据键值生成规则为数据源每个数组项生成唯一键值并创建组件。
  • 示例
class BasicDataSource implements IDataSource {// 省略部分代码...
}class MyDataSource extends BasicDataSource {// 省略部分代码...
}@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}}, (item: string) => item)}.cachedCount(5)}
}
  • 上述代码中,键值生成规则为item,为数据源数组项依次生成键值Hello 0Hello 1等,并创建对应的ListItem子组件渲染到界面。

  • 键值相同时错误渲染:不同数据项生成相同键值时,框架行为不可预测。例如,当所有数据项键值相同时,滑动过程中可能因框架取用缓存错误导致子组件渲染问题。

2. 非首次渲染

  • 当数据源变化时,开发者需根据变化情况调用listener对应的接口通知LazyForEach更新,包括添加、删除、交换数据和改变单个数据等操作。

  • 添加数据

class BasicDataSource implements IDataSource {// 省略部分代码...
}class MyDataSource extends BasicDataSource {// 省略部分代码...
}@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}.onClick(() => {// 点击追加子组件this.data.pushData(`Hello ${this.data.totalCount()}`);})}, (item: string) => item)}.cachedCount(5)}
}
  • 点击子组件时,调用数据源的pushData方法添加数据并通知LazyForEach,LazyForEach会在相应索引处新建子组件。

  • 删除数据

class BasicDataSource implements IDataSource {// 省略部分代码...
}class MyDataSource extends BasicDataSource {// 省略部分代码...
}@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}.onClick(() => {// 点击删除子组件this.data.deleteData(this.data.dataArray.indexOf(item));})}, (item: string) => item)}.cachedCount(5)}
}
  • 点击子组件时,调用数据源的deleteData方法删除数据并通知LazyForEach,LazyForEach会在相应索引处删除子组件。

  • 交换数据

class BasicDataSource implements IDataSource {// 省略部分代码...
}class MyDataSource extends BasicDataSource {// 省略部分代码...
}@Entry
@Component
struct MyComponent {private moved: number[] = [];private data: MyDataSource = new MyDataSource();aboutToAppear() {for (let i = 0; i <= 20; i++) {this.data.pushData(`Hello ${i}`)}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("appear:" + item)})}.margin({ left: 10, right: 10 })}.onClick(() => {this.moved.push(this.data.dataArray.indexOf(item));if (this.moved.length === 2) {// 点击交换子组件this.data.moveData(this.moved[0], this.moved[1]);this.moved = [];}})}, (item: string) => item)}.cachedCount(5)}
}
  • 首次点击子组件记录索引,再次点击时调用数据源的moveData方法移动数据并通知LazyForEach,LazyForEach会交换相应索引处的子组件位置。

  • 改变单个数据(示例代码未完整给出改变单个数据的通知逻辑,假设已有相应方法):类似其他操作,需在数据源中修改数据并通过合适的listener方法通知LazyForEach,以触发对应子组件的更新。

四、常见使用问题

1. 渲染结果非预期

  • 如键值生成规则不合理或数据源操作与通知不匹配,可能导致渲染结果不符合预期。

2. 重渲染时图片闪烁

  • 可能由于组件更新过程中图片加载或处理不当导致,需优化图片相关操作或检查组件更新逻辑。

3. @ObjectLink属性变化UI未更新

  • 可能是未正确使用相关装饰器或数据绑定机制,需确保@ObjectLink等装饰器的正确使用及数据的正确传递与更新。

4. 在List内使用屏幕闪烁

  • 可能与List组件的配置、LazyForEach的更新机制或其他因素有关,可检查List组件属性设置、数据更新频率及设备性能等方面。

使用LazyForEach时,需严格遵循其使用限制,合理定义键值生成规则,正确处理数据源变化通知,同时注意常见问题的排查与解决,以充分发挥其数据懒加载优势,提升应用性能与用户体验。


http://www.ppmy.cn/server/151333.html

相关文章

【PHP小课堂】在PHP中使用Zookeeper

在PHP中使用Zookeeper 不知道大家对于 Zookeeper 的了解有多少&#xff0c;我在实际的项目中没有使用过&#xff0c;但是之前学过一点。因此&#xff0c;今天我们只来看看 PHP 中关于 Zookeeper 的扩展相关函数的使用&#xff0c;不会涉及更加深入的 Zookeeper 相关概念和细节的…

【从零开始入门unity游戏开发之——C#篇12】新的引用类型——数组array

文章目录 一、数组&#xff08;array&#xff09;&#xff08;一维数组&#xff09;1、声明数组2、初始化数组3、访问数组元素4 、修改数组元素5、获取数组的长度6、遍历数组使用 for 循环&#xff1a;使用 foreach 循环&#xff1a; 7、数组方法排序&#xff1a;逆序&#xff…

9 OOM和JVM退出。OOM后JVM一定会退出吗?

首先我们把两个概念讲清楚 OOM是线程在申请堆内存&#xff0c;发现堆内存空间不足时候抛出的异常。 JVM退出的条件如下&#xff1a; java虚拟机在没有守护线程的时候会退出。守护线程是启动JVM的线程&#xff0c;服务于用户线程。 我们简单说下守护线程的功能: 1.日志的记录…

DeepSeek-AI 开源 DeepSeek-VL2 系列,采用专家混合(MoE)架构,重新定义视觉语言人工智能

将视觉与语言的智能融合&#xff0c;已经在视觉语言模型&#xff08;Vision-Language Models&#xff0c;简称VLMs&#xff09;领域实现了重大突破。这些模型致力于同步处理和解释视觉与文本数据&#xff0c;从而使得图像描述、视觉问题回答、光学字符识别&#xff08;Optical …

ChatGPT崩溃引发行业震动:智能化之路需多元发展

今晨&#xff0c;当我如常打开ChatGPT&#xff0c;准备开始一天的工作时&#xff0c;却遭遇了令人措手不及的崩溃。起初&#xff0c;我还天真地以为这只是区域性的网络波动或是账号的小故障&#xff0c;于是费尽心思地清除浏览器缓存、cookies&#xff0c;甚至尝试更换区域设置…

大数据新视界 -- 大数据大厂之 Impala 性能飞跃:动态分区调整的策略与方法(上)(21 / 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

ElasticSearch 数据聚合与运算

1、数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现数据的统计、分析和运算。实现这些统计功能的比数据库的 SQL 要方便的多&#xff0c;而且查询速度非常快&#xff0c;可以实现近实时搜索效果。 注意&#xff1a; 参加聚合的字段必须是 keywor…

【linux】shell(37)-脚本调试

1. 使用 Shell 调试选项 Shell 提供了多种调试选项&#xff0c;可以用于检查脚本的语法和执行过程。 1.1 -n 选项 作用&#xff1a;读取脚本但不执行&#xff0c;用于检查脚本的语法错误。 用法&#xff1a; bash -n script.sh示例&#xff1a; #!/bin/bash echo "H…