在 ArkTS 的开发中,如果你要渲染一个很长的列表,比如商品列表、评论列表或者朋友圈动态,用传统的循环结构(比如 ForEach)很容易导致性能问题,尤其是加载慢、卡顿甚至内存暴涨。

这时候就要用到 懒加载渲染组件——LazyForEach

LazyForEach 是 ArkTS 提供的一种 延迟渲染列表项的方式。只有当列表项真正要被显示在屏幕上时,相关组件才会被创建和渲染,从而节省内存和提升性能。

可以把它理解成 ArkTS 中的“虚拟滚动列表”。

LazyForEach的使用限制

  • LazyForEach必须在容器组件内使用仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载(可配置cachedCount属性,即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。支持数据懒加载的父组件根据自身及子组件的高度或宽度计算可视区域内需布局的子节点数量,高度或宽度的缺失会导致部分场景懒加载失效

catchdCount

List设置cachedCount后,显示区域外上下各会预加载并布局cachedCount行ListItem。

  • LazyForEach依赖生成的键值判断是否刷新子组件,键值不变则不触发刷新。
  • 容器组件内只能包含一个LazyForEach。以List为例,不推荐同时包含ListItem、ForEach、LazyForEach。也不推荐同时包含多个LazyForEach。
  • LazyForEach在每次迭代中,必须创建且只允许创建一个子组件;即LazyForEach的子组件生成函数有且只有一个根组件
  • 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
  • 允许LazyForEach包含在if/else条件渲染语句中,也允许LazyForEach中出现if/else条件渲染语句。
  • 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件渲染出现问题。
  • LazyForEach必须使用DataChangeListener对象进行更新。重新赋值第一个参数dataSource会导致异常;dataSource使用状态变量时,状态变量改变不会触发LazyForEach的UI刷新
  • 为了高性能渲染,使用DataChangeListener对象的onDataChange方法更新UI时,需要生成不同于原来的键值来触发组件刷新。

LazyForEach键值生成规则

LazyForEach提供了参数keyGenerator,开发者可以使用该函数生成自定义键值。如果未定义keyGenerator函数,ArkUI框架将使用默认的键值生成函数:(item: Object, index: number) => { return viewId + ‘-’ + index.toString(); }。viewId在编译器转换过程中生成,同一个LazyForEach组件内的viewId一致。

基本语法

LazyForEach(dataSource: IDataSource,             // 需要进行数据迭代的数据源itemGenerator: (item: any, index?: number) => void,  // 子组件生成函数keyGenerator?: (item: any, index?: number) => string // 键值生成函数
): void
  • dataSource:IDataSource

    LazyForEach数据源,需要开发者实现相关接口

  • itemGenerator:(item: any, index: number) => void

    子组件生成函数,为数组中的每一个数据项创建一个子组件。

  • keyGenerator: (item: any, index: number) => string

    键值生成函数,用于给数据源中的每一个数据项生成唯一且固定的键值。修改数据源中的一个数据项若不影响其生成的键值,则对应组件不会被更新,否则此处组件就会被重建更新。keyGenerator参数是可选的,但是,为了使开发框架能够更好地识别数组更改并正确更新组件,建议提供

实现IDataSource接口

interface IDataSource {totalCount(): number; // 获得数据总数getData(index: number): Object; // 获取索引值对应的数据registerDataChangeListener(listener: DataChangeListener): void; // 注册数据改变的监听器unregisterDataChangeListener(listener: DataChangeListener): void; // 注销数据改变的监听器
}

DataChangeListener接口

interface DataChangeListener {onDataReloaded(): void; // 重新加载数据完成后调用onDataAdded(index: number): void; // 添加数据完成后调用onDataMoved(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换完成后调用onDataDeleted(index: number): void; // 删除数据完成后调用onDataChanged(index: number): void; // 改变数据完成后调用onDataAdd(index: number): void; // 添加数据完成后调用onDataMove(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换完成后调用onDataDelete(index: number): void; // 删除数据完成后调用onDataChange(index: number): void; // 改变数据完成后调用
}

创建LazyForEach

首次渲染

在LazyForEach首次渲染时,会根据上述键值生成规则为数据源的每个数组项生成唯一键值并创建相应的组件。

局部代码如下:

class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@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) // 设置显示区域外上下预加载布局}
}

渲染结果如图

在这里插入图片描述

非首次渲染

当LazyForEach数据源发生变化,需要再次渲染时,开发者应根据数据源的变化情况调用listener对应的接口,通知LazyForEach做相应的更新。

局部代码如下:

class MyDataSource extends BasicDataSource {private dataArray: string[] = [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}@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)}
}

渲染结果如图:
在这里插入图片描述

LazyForEach之前:先实现IDataSource接口

// 实现IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];private originDataArray: string[] = [];public totalCount(): number {return this.originDataArray.length;}public getData(index: number): string {return this.originDataArray[index];}// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener');this.listeners.push(listener);}}// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('remove listener');this.listeners.splice(pos, 1);}}// 通知LazyForEach组件需要重载所有子组件notifyDataReload(): void {this.listeners.forEach(listener => {listener.onDataReloaded();});}// 通知LazyForEach组件需要在index对应索引处添加子组件notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index);// 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);});}// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index);// 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);});}// 通知LazyForEach组件需要在index对应索引处删除该子组件notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index);// 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);});}// 通知LazyForEach组件将from索引和to索引处的子组件进行交换notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {listener.onDataMove(from, to);// 写法2:listener.onDatasetChange(//         [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);});}notifyDatasetChange(operations: DataOperation[]): void {this.listeners.forEach(listener => {listener.onDatasetChange(operations);});}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93443.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93443.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/93443.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态规划:从入门到精通

本文全章节一共一万七千多字&#xff0c;详细介绍动态规划基础与进阶技巧&#xff0c;全篇以代码为主&#xff0c;认真读完理解&#xff0c;你对动态规划的理解一定会有一个质的飞跃。一、动态规划简介: 动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&…

八股训练营 40 天心得:一场结束,也是一场新的开始

八股训练营 40 天心得&#xff1a;一场结束&#xff0c;也是一场新的开始 感谢卡哥的训练营组织卡码笔记&#xff0c;对即将参加秋招的我们帮助了很多&#xff0c;感谢卡哥的开源代码随想录代码随想录 四十天前&#xff0c;我带着一颗不安却坚定的心&#xff0c;踏入了这场“…

STM32系统定时器(SysTick)详解:从原理到实战的精确延时与任务调度

前言&#xff1a;为什么SysTick是嵌入式开发的"瑞士军刀"&#xff1f; 在STM32开发中&#xff0c;我们经常需要精确的延时功能&#xff08;如毫秒级延时控制LED闪烁&#xff09;或周期性任务调度&#xff08;如定时采集传感器数据&#xff09;。实现这些功能的方式有…

【微信小程序】12、生物认证能力

1、生物认证 生物认证 是一种基于个体独特生理或行为特征进行身份验证的技术,广泛应用于安全、金融、医疗等领域。 小程序目前暂时只支持指纹识别认证。 2、查询支持的生物认证方式 获取本机支持的 SOTER 生物认证方式&#xff0c;文档 onLoad(options) {wx.checkIsSuppor…

高级机器学习

机器学习常见方法涉及方法&#xff1a;2.半监督学习3.无监督学习4.度量学习5.迁移学习6.多示例多标记学习7.在线学习8.元学习9.联邦学习10.强化学习11.概率图模型独立同分布独立指的是&#xff0c;样本集包括训练集测试集的任意两个样本之间都是不相关的。在表示样本的特征确定…

Chrome 提示 “此扩展程序不再受支持”(MacOS/Windows)

原因 最新 Chrome 使用 Manifest V3, 并在新版浏览器中 停止 V2 支持 处理方法 MacOS 新建一个后缀为 .mobileconfig 的文件, 内容参考 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN&…

C++20协程实战:高效网络库、手机终端、多媒体开发开发指南

基于C++协程和事件循环的网络库 以下是基于C++协程和事件循环的网络库实例,涵盖常见场景和功能实现。示例基于libuv、Boost.Asio或自定义事件循环,结合C++20协程(如std::coroutine)或其他协程库(如cppcoro)实现。 基础TCP服务器 #include <cppcoro/task.hpp> #in…

数据库4.0

索引 事务 JDBC~ 目录 一、MySQL索引 1.0 概述 2.0 相关操作 3.0 注意 4.0 索引背后的原理的理解 二、 事务 1.0 原子性 2.0 隔离性 (1)并发执行 (2) 出现的问题 3.0 使用 三、JDBC编程 1.0 概述 2.0 如何下载驱动包 3.0 jar如何引入到项目之中 4.0 jdbc…

HarmonyOS-ArkUI Web控件基础铺垫6--TCP协议- 流量控制算法与拥塞控制算法

HarmonyOS-ArkUI Web控件基础铺垫1-HTTP协议-数据包内容-CSDN博客 HarmonyOS-ArkUI Web控件基础铺垫2-DNS解析-CSDN博客 HarmonyOS-ArkUI Web控件基础铺垫3--TCP协议- 从规则本质到三次握手-CSDN博客 HarmonyOS-ArkUI Web控件基础铺垫4--TCP协议- 断联-四次挥手解析-CSDN博客…

Dify 从入门到精通(2/100 篇):Dify 的核心组件 —— 从节点到 RAG 管道

Dify 的核心组件&#xff1a;从节点到 RAG 管道 引言 在 Dify 博客系列&#xff1a;从入门到精通&#xff08;100 篇&#xff09; 的第一篇《Dify 究竟是什么&#xff1f;真能开启低代码 AI 应用开发的未来&#xff1f;》中&#xff0c;我们全面介绍了 Dify 的定位、核心特点…

在线培训、远程示教——医疗器械行业的直播解决方案

文章目录前言一、医疗器械直播应用的两大核心场景二、直播平台在医疗场景中的关键技术支持点三、典型功能实现原理总结前言 医疗器械行业对“培训”和“示教”的专业性要求极高&#xff0c;传统的线下模式常因时间、空间、人员成本等受限而效率低下。而随着高清低延迟视频技术…

Mqttnet的MqttClientTlsOptions.CertificateValidationHandler详解

MqttClientTlsOptions.CertificateValidationHandler 是 MQTTnet 库中用于自定义 TLS 证书验证逻辑的关键回调函数。在 MQTT 客户端与服务器建立 TLS 连接时&#xff0c;该回调允许你覆盖默认的证书验证流程&#xff0c;实现自定义的安全策略。核心作用当 MQTT 客户端通过 TLS …

【图像噪点消除】——图像预处理(OpenCV)

目录 1 均值滤波 2 方框滤波 3 高斯滤波 4 中值滤波 5 双边滤波 6 小结 噪声&#xff1a;图像中的一些干扰因素。通常是由于图像采集设备、传输信道等因素造成的&#xff0c;表现为图像中随机的亮度。常见的噪声类型有高斯噪声和椒盐噪声。高斯噪声是一种分布符合正态分布…

Vulnhub napping-1.0.1靶机渗透攻略详解

一、下载靶机 下载地址&#xff1a;https://download.vulnhub.com/napping/napping-1.0.1.ova 下载好后使用VM打开&#xff0c;将网络配置模式改为net&#xff0c;防止桥接其他主机干扰&#xff08;桥接Mac地址也可确定主机&#xff09;。 二、发现主机 使用nmap扫描没有相应…

Kubernetes自动扩容方案

Kubernetes 自动扩容可以概括为 “三层六类”&#xff1a;层级类型触发维度官方/社区方案一句话说明Pod 级HPACPU / 内存 / 自定义 / 外部指标内置副本数横向扩缩&#xff0c;最常用VPACPU / 内存社区组件单 Pod 资源竖向扩缩&#xff0c;不改副本数KEDA任意事件&#xff08;队…

linux命令ps的实际应用

ps&#xff08;Process Status&#xff09;是 ​Linux/Unix 系统中最核心的进程管理工具&#xff0c;用于实时抓取系统进程快照。它直接读取 /proc 文件系统&#xff0c;不持续监控进程&#xff08;区别于 top&#xff09;&#xff0c;但可通过参数组合实现精准进程诊断。下面从…

深入理解C语言:详解直接插入排序的实现与优化

目录 引言 一、直接插入排序的相关概念 1.1、基本概念 1.2、直接插入排序过程详解 二、代码实现 三、时间复杂度 四、希尔排序 4.1、希尔排序的陈述 4.2、代码实现 4.3、时间复杂度 结语 引言 在计算机科学的世界里&#xff0c;排序算法是基础且重要的组成部分。它们…

【DRAM存储器五十五】LPDDR5介绍--command bus training

👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5A》 在为高频或中频操作启用ODT之前,必须对L

一道曾经百度面试题

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入1. 题目重现2. 大小端到底在比什么&#xff1f;3. 解法一&#xff1a;联合体&#xff08;union&#xff09;为什么一行就够&#xff1f;使用示例4. 解法二&am…

VIKOR(Multi-criteria Optimization and Compromise Solution)简介与简单示例

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…