HarmonyOS NEXT应用开发之ForEach:循环渲染

ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为 List组件 。

说明:
从API version 9开始,该接口支持在ArkTS卡片中使用。

接口描述

ForEach(
  arr: Array,
  itemGenerator: (item: any, index: number) => void,
  keyGenerator?: (item: any, index: number) => string
)

以下是参数的详细说明:

参数名 参数类型 是否必填 参数描述
arr Array 数据源,为Array类型的数组。
说明:
- 可以设置为空数组,此时不会创建子组件。
- 可以设置返回值为数组类型的函数,例如arr.slice(1, 3),但设置的函数不应改变包括数组本身在内的任何状态变量,例如不应使用Array.splice(),Array.sort()Array.reverse()这些会改变原数组的函数。
itemGenerator (item: any, index: number) => void 组件生成函数。
- 为数组中的每个元素创建对应的组件。
- item参数:arr数组中的数据项。
- index参数(可选):arr数组中的数据项索引。
说明:
- 组件的类型必须是ForEach的父容器所允许的。例如,ListItem组件要求ForEach的父容器组件必须为List组件。
keyGenerator (item: any, index: number) => string 键值生成函数。
- 为数据源arr的每个数组项生成唯一且持久的键值。函数返回值为开发者自定义的键值生成规则。
- item参数:arr数组中的数据项。
- index参数(可选):arr数组中的数据项索引。
说明:
- 如果函数缺省,框架默认的键值生成函数为(item: T, index: number) => { return index + '__' + JSON.stringify(item); }
- 键值生成函数不应改变任何组件状态。

说明:

  • ForEachitemGenerator函数可以包含if/else条件渲染逻辑。另外,也可以在if/else条件渲染语句中使用ForEach组件。
  • 在初始化渲染时,ForEach会加载数据源的所有数据,并为每个数据项创建对应的组件,然后将其挂载到渲染树上。如果数据源非常大或有特定的性能需求,建议使用LazyForEach组件。

键值生成规则

ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。

ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即(item: any, index: number) => { return index + '__' + JSON.stringify(item); }

ArkUI框架对于ForEach的键值生成有一套特定的判断规则,这主要与itemGenerator函数的第二个参数index以及keyGenerator函数的第二个参数index有关,具体的键值生成规则判断逻辑如下图所示。

图1 ForEach键值生成规则

说明:
ArkUI框架会对重复的键值发出警告。在UI更新的场景下,如果出现重复的键值,框架可能无法正常工作。

组件创建规则

在确定键值生成规则后,ForEach的第二个参数itemGenerator函数会根据键值生成规则为数据源的每个数组项创建组件。组件的创建包括两种情况: ForEach首次渲染 和 ForEach非首次渲染 。

首次渲染

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

@Entry
@Component
struct Parent {
   
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
   
    Row() {
   
      Column() {
   
        ForEach(this.simpleList, (item: string) => {
   
          ChildItem({
    item: item })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
   
  @Prop item: string;

  build() {
   
    Text(this.item)
      .fontSize(50)
  }
}

运行效果如下图所示。

图2 ForEach数据源不存在相同值案例首次渲染运行效果图

在上述代码中,键值生成规则是keyGenerator函数的返回值item。在ForEach渲染循环时,为数据源数组项依次生成键值onetwothree,并创建对应的ChildItem组件渲染到界面上。

当不同数组项按照键值生成规则生成的键值相同时,框架的行为是未定义的。例如,在以下代码中,ForEach渲染相同的数据项two时,只创建了一个ChildItem组件,而没有创建多个具有相同键值的组件。

@Entry
@Component
struct Parent {
   
  @State simpleList: Array<string> = ['one', 'two', 'two', 'three'];

  build() {
   
    Row() {
   
      Column() {
   
        ForEach(this.simpleList, (item: string) => {
   
          ChildItem({
    item: item })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
   
  @Prop item: string;

  build() {
   
    Text(this.item)
      .fontSize(50)
  }
}

运行效果如下图所示。

图3 ForEach数据源存在相同值案例首次渲染运行效果图

在该示例中,最终键值生成规则为item。当ForEach遍历数据源simpleList,遍历到索引为1的two时,按照最终键值生成规则生成键值为two的组件并进行标记。当遍历到索引为2的two时,按照最终键值生成规则当前项的键值也为two,此时不再创建新的组件。

非首次渲染

在ForEach组件进行非首次渲染时,它会检查新生成的键值是否在上次渲染中已经存在。如果键值不存在,则会创建一个新的组件;如果键值存在,则不会创建新的组件,而是直接渲染该键值所对应的组件。例如,在以下的代码示例中,通过点击事件修改了数组的第三项值为"new three",这将触发ForEach组件进行非首次渲染。

@Entry
@Component
struct Parent {
   
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
   
    Row() {
   
      Column() {
   
        Text('点击修改第3个数组项的值')
          .fontSize(24)
          .fontColor(Color.Red)
          .onClick(() => {
   
            this.simpleList[2] = 'new three';
          })

        ForEach(this.simpleList, (item: string) 
回答: 在使用ForEach循环渲染时,可以通过遍历数组的方式获取每个数组的值,并对其进行判断是否仍然是数组。如果不是数组,则直接输出。如果是数组,则可以使用嵌套的ForEach循环对该数组进行第二次遍历。在JSP中,还可以使用EL函数来输出总数,而不需要引入scriplet代码。EL函数允许在公共类中调用public static方法,例如定义一个名为sum(List people)的公共静态方法。在tld文件中,可以使用以下声明来调用EL函数。在渲染过程中,可以使用HTML标签来展示数据,例如使用<td align="center"><a href="***.do?yxdm=<c:out value='${yxdm.key}'/>"><c:out value="${yxdm.value}"/></a></td>来渲染数据。 #### 引用[.reference_title] - *1* [php页面渲染](https://blog.csdn.net/brokenkay/article/details/90403939)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [jsp – 计算c:forEach循环中所有数字的总和](https://blog.csdn.net/weixin_39785422/article/details/118051383)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [JSTL之<c:foreach>循环展示table](https://blog.csdn.net/weixin_33698043/article/details/92951794)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值