鸿蒙开发:ForEach中为什么键值生成函数很重要
前言
在列表组件使用的时候,如List、Grid、WaterFlow等,循环渲染时都会使用到ForEach或者LazyForEach,当然了,也有单独使用的场景,如下,一个很简单的列表组件使用,这种使用方式,在官方的很多案例中也多次出现,相信在实际的开发中多多少少也会存在。
List({ space: 20, initialIndex: 0 }) {ForEach(["条目1", "条目2", "条目3", "条目4", "条目5", "条目6"], (item: string) => {ListItem() {Text(item).width('100%').height(50).fontSize(16).fontColor(Color.White).textAlign(TextAlign.Center).backgroundColor(Color.Orange)}}, (item: string) => item)}.padding({ left: 20, right: 20 })
以上的代码,看上去也没啥问题,UI也能正常的展示出来,如下图:

仿佛这一切都是正确的,但是,以上的代码会存在一定的问题,那就是渲染非预期,我们继续验证问题所在,增加一个按钮,用来添加数据,当然了这里需要把数据源提取至成员变量,并用@State装饰器进行修饰:
@State list: string[] = ["条目1", "条目2", "条目3", "条目4", "条目5", "条目6"]build() {Column() {Button("追加数据").onClick(() => {this.list.push("条目七")this.list.push("条目八")})List({ space: 20, initialIndex: 0 }) {ForEach(this.list, (item: string) => {ListItem() {Text(item).width('100%').height(50).fontSize(16).fontColor(Color.White).textAlign(TextAlign.Center).backgroundColor(Color.Orange)}}, (item: string) => item)}.padding({ left: 20, right: 20 }).margin({ top: 20 })}}

当我们点击追加数据按钮时,正常的情况会是,数组中增加数据,驱动UI更新,List组件应该会增加【条目七,条目八】两条数据。
确实,点击后,UI发生了变化,列表中增加了两条数据:

有问题吗?说了一大堆,程序这不执行挺正常的,哎,稍安勿躁,我们再次点击一下,正常的程序,会再次增加两条数据,对吧?
但是,问题来了,没有增加!!!,点击一百次也没有增加。
难道是重复的数据不能重复添加?这就很扯了吧,列表中不能出现重复的数据,这在任何一个系统中都是闻所未闻的奇观。
显然这些问题都不是,问题的原因就在于,循环的第三个参数:keyGenerator。
本文的主要内容如下:
1、了解循环ForEach/LazyForEach三个参数
2、了解键值生成规则
3、禁止渲染非预期情况
4、正确使用键值
5、使用相关总结
一、了解循环ForEach/LazyForEach三个参数
ForEach
(arr: Array<any>, itemGenerator: (item: any, index: number) => void, keyGenerator?: (item: any, index: number) => string): ForEachAttribute;
LazyForEach
(dataSource: IDataSource, itemGenerator: (item: any, index: number) => void, keyGenerator?: (item: any, index: number) => string): LazyForEachAttribute;
第一个参数arr/dataSource是数据源,用来渲染UI的数据,非常重要,渲染多少数据,动态增加数据,都是和它有着直接的关系,可以是任何类型的数组源,比如对象,字符串,数值,都可以。
第二个参数itemGenerator,是组件生成函数,目的为数组中的每个元素创建对应的组件,它是和第一个数据源是一一对应的。
第三个参数keyGenerator,是键值生成函数,为数据源arr的每个数组项生成唯一且持久的键值,其返回值,可以自己定义,如果自己定义,一定要是唯一的,如果不定义,会是默认的:(item: T, index: number) => { return index + '__' + JSON.stringify(item); },默认的也能满足大部分的需求,所以,在实际的开发中,如果你很难决定唯一,那么直接用默认的就行。在前言中的问题,就是因为键值不唯一造成的。
二、了解键值生成规则

通过了解循环的三个参数,我们已经知道了,系统会为我们提供设置键值的函数参数,可以使用自定义的,当然也可以使用默认的键值生成规则,也就是item: Object, index: number) => { return index + '__' + JSON.stringify(item); }。
在实际的渲染过程中,每个数组元素生成一个唯一且持久的键值,用来标记相对应的组件,当键值有变化时,ArkUI框架会认为,当前数组元素替换或修改,会根据新的键值重新创建一个新的组件。
键值的生成规则,直接会影响着数据渲染的UI,因为第二个参数itemGenerator函数会根据键值生成规则为数据源的每个数组项创建组件。
在前言的Demo中,可以发现,每个组件的键值为当前的数据源,当不同数组项按照键值生成规则生成的键值相同时,框架认为是未定义的,此时不再创建新的组件,也就是点击不会再次创建组件的原因。
当然了,还有一种情况,那就是,在已有的数据上进行修改,比如有三条数据,把第三条数据修改为新的数据源,这种情况,前两个数据,ForEach会复用进行渲染,第三个则会为该数组项创建了一个新的组件。
三、禁止渲染非预期情况
什么叫渲染非预期?前言中的Demo就是一个典型的案例,存在相同键值,因此不会创建新组件,在实际的开发中,使用ForEach时应尽量避免最终键值生成规则中包含index,或者使用不唯一的规则作为键值。
四、正确使用键值
首先,必须满足键值的唯一性,这一点毋庸置疑,必须要设置正确,如果使用的是对象,强烈建议,使用对象中的唯一值,比如id作为键值。
如果是使用基本类型的数据作为键值,一定要确保数组中的元素是没有重复的,否则就会出现前言Demo中的问题,另外,在使用基本类型键值,ForEach在改变数据源后会重新创建组件,这会带来一定的性能损耗问题。
根据官方的解读,在使用ForEach的时候,尽量不要与LazyForEach混合使用,这是官方所不推荐的,切记!
五、使用相关总结
为了使得数据渲染正确,请一定要确保第三个参数键值的唯一性,另外除非必要,不推荐将第三个参数KeyGenerator函数处于缺省状态,以及在键值生成规则中包含数据项索引index。
相关文章:
鸿蒙开发:ForEach中为什么键值生成函数很重要
前言 在列表组件使用的时候,如List、Grid、WaterFlow等,循环渲染时都会使用到ForEach或者LazyForEach,当然了,也有单独使用的场景,如下,一个很简单的列表组件使用,这种使用方式,在官…...
沃丰科技智能外呼机器人:超越人工,重塑外呼体验
随着科技的不断发展,人工智能已经逐渐渗透到各行各业,其中智能外呼机器人的出现,更是给企业带来了全新的客户体验。与传统的人工外呼相比,智能外呼机器人具有更高的效率、更低的成本以及更好的用户体验等优势。 优势一࿱…...
百度飞浆:paddle 线性回归模型
学习引用 参考视频: https://www.bilibili.com/video/BV1oRtkeVEVx?spm_id_from333.788.player.switch&vd_sourcec7739de98d044e74cdc74d6e772bed5f&p2 这段代码使用PaddlePaddle深度学习框架来实现一个简单的线性回归模型,旨在从给定的出租车…...
【JavaSE】【网络编程】UDP数据报套接字编程
目录 一、网络编程简介二、Socket套接字三、TCP/UDP简介3.1 有连接 vs 无连接3.2 可靠传输 vs 不可靠传输3.3 面向字节流 vs 面向数据报3.4 双向工 vs 单行工 四、UDP数据报套接字编程4.1 API介绍4.1.1 DatagramSocket类4.1.1.1 构造方法4.1.1.2 主要方法 4.1.2 DatagramPocket…...
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
点赞收藏加关注,你也能主打别墅! 一、问题描述 Mac上终端运行如下命令: sudo npm install typescript -g //全局安装ts提示成功安装后,我测试tsc -v这个命令时出现如下错误: 也就是说找不到 tsc 命令。 二、解决方…...
20241120-Milvus向量数据库快速体验
目录 20241120-Milvus向量数据库快速体验Milvus 向量数据库pymilvus内嵌向量数据库模式设置向量数据库创建 Collections准备数据用向量表示文本插入数据 语义搜索向量搜索带元数据过滤的向量搜索查询通过主键搜索 删除实体加载现有数据删除 Collections了解更多 个人主页: 【⭐…...
【Golang】——Gin 框架中间件详解:从基础到实战
中间件是 Web 应用开发中常见的功能模块,Gin 框架支持自定义和使用内置的中间件,让你在请求到达路由处理函数前进行一系列预处理操作。这篇博客将涵盖中间件的概念、内置中间件的用法、如何编写自定义中间件,以及在实际应用中的一些最佳实践。…...
量子计算来袭:如何保护未来的数字世界
目录 前言 一、量子计算安全的学习方向 1. 量子物理学基础 2. 量子计算原理与技术 3. 传统网络安全知识 4. 量子密码学 5. 量子计算安全政策与法规 二、量子计算的漏洞风险 1. 加密算法被破解风险 2. 区块链安全风险 3. 量子密钥分发风险 4. 量子计算系统自身风险 …...
VMware虚拟机(Ubuntu或centOS)共享宿主机网络资源
VMware虚拟机(Ubuntu或centOS)共享宿主机网络资源 由于需要在 Linux 环境下进行一些测试工作,于是决定使用 VMware 虚拟化软件来安装 Ubuntu 24.04 .1操作系统。考虑到测试过程中需要访问 Github ,要使用Docker拉去镜像等外部网络资源,因此产…...
光伏电站仿真系统的作用
光伏仿真系统有多方面的重要作用,不仅对前期的项目设计评估还是后期的运维效验都有非常重要的作用。 1、优化系统设计 通过输入不同的光伏组件参数、布局方案以及气象条件等,模拟各种设计场景下光伏电站的性能表现。例如,可以比较不同类型光…...
Golang文件操作
写文件 &emsp; os模块可以创建文件,使用fmt可以写入文件。如以下例子: package mainimport ("fmt""os" )func main() {// 学习 golang的文件操作file, err : os.Create("test.txt")if err ! nil {fmt.P…...
爬虫开发工具与环境搭建——使用Postman和浏览器开发者工具
第三节:使用Postman和浏览器开发者工具 在网络爬虫开发过程中,我们经常需要对HTTP请求进行测试、分析和调试。Postman和浏览器开发者工具(特别是Network面板和Console面板)是两种最常用的工具,能够帮助开发者有效地捕…...
React(二)
文章目录 项目地址七、数据流7.1 子组件传递数据给父组件7.1.1 方式一:給父设置回调函数,传递给子7.1.2 方式二:直接将父的setState传递给子7.2 给props传递jsx7.2.1 方式一:直接传递组件给子类7.2.2 方式二:传递函数给子组件7.3 props类型验证7.4 props的多层传递7.5 cla…...
同步原语(Synchronization Primitives)
同步原语(Synchronization Primitives)是用于控制并发编程中多个线程或进程之间的访问顺序,确保共享资源的安全访问的一组机制或工具。它们解决了竞争条件(Race Condition)、死锁(Deadlock)等并…...
SpringBoot服务多环境配置
一个项目的的环境一般有三个:开发(dev)、测试(test)、生产(proc),一般对应三套环境,三套配置文件。 像下面这样直接写两个配置文件是不行的。 application.ymlserver:port: 8080application-dev.ymlspring:datasource:driver-class-name: co…...
STM32单片机CAN总线汽车线路通断检测-分享
目录 目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 1.电路图采用Altium Designer进行设计: 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着汽车电子技术的不断发展,车辆通信接口在汽车电子控…...
【环境搭建】使用IDEA远程调试Docker中的Java Web
有时候要对Docker的Java Web远程调试其功能,于是就需要使用IDEA的远程调试功能,记录一下简单配置方法。 以Kylin4.0.0为例,首先拉取镜像并启动容器: $ docker pull apachekylin/apache-kylin-standalone:4.0.0$ docker run -d \-…...
贴代码框架PasteForm特性介绍之select,selects,lselect和reload
简介 PasteForm是贴代码推出的 “新一代CRUD” ,基于ABPvNext,目的是通过对Dto的特性的标注,从而实现管理端的统一UI,借助于配套的PasteBuilder代码生成器,你可以快速的为自己的项目构建后台管理端!目前管…...
STM32G4的数模转换器(DAC)的应用
目录 概述 1 DAC模块介绍 2 STM32Cube配置参数 2.1 参数配置 2.2 项目架构 3 代码实现 3.1 接口函数 3.2 功能函数 3.3 波形源代码 4 DAC功能测试 4.1 测试方法介绍 4.2 波形测试 概述 本文主要介绍如何使用STM32G4的DAC模块功能,笔者使用STM32Cube工具…...
SpringMVC跨线程获取requests请求对象(子线程共享servletRequestAttributes)和跨线程获取token信息
文章目录 引言I 跨线程共享数据跨线程获取requests请求对象基于org.slf4j.MDC存储共享数据InheritableThreadLocal解决异步线程,无法获取token信息问题II Feign 传递请求属性feign 模块处理被调用方处理请求头III 异步调用的方式CompletableFutureAsync注解Executors引言 本文…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
