写给初学者的 HarmonyOS 教程 -- 循环渲染(ForEach)

本文详细解释了如何在IT技术中使用ForEach接口,尤其是在与数组数据和容器组件配合时。讲解了接口参数、键值生成规则、组件创建过程,以及首次渲染和非首次渲染的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一、接口描述

ForEach(
  arr: Array,
  itemGenerator: (item: Array, index?: number) => void,
  keyGenerator?: (item: Array, index?: number): string => string
)
参数名参数类型必填参数描述
arrArrayYes数据源,为 Array 类型的数组

说明:
⇒ 可以设置为空数组,此时不会创建子组件
⇒ 可以设置返回值为数组类型的函数,例如arr.slice(1, 3)

但设置的函数不应改变包括数组本身在内的任何状态变量,
例如不应使用 Array.splice(),Array.sort() 或Array.reverse()
这些会改变原数组的函数。
itemGenerator(item: any, index?: number) => voidYes组件生成函数

⇒ 为数组中的每个元素创建对应的组件

参数:
⇒ item:arr 数组中的数据项
⇒ index:arr 数组中的数据项索引(可选)

说明:
⇒ 组件的类型必须是 ForEach 的父容器所允许的。
例如,ListItem 组件要求 ForEach 的父容器组件必须为 List 组件。
keyGenerator(item: any, index?: number) => stringNo键值生成函数

⇒ 为数据源 arr 的每个数组项生成唯一且持久的键值,
函数返回值为开发者自定义的键值生成规则。

参数:
⇒ item:arr 数组中的数据项
⇒ index:arr 数组中的数据项索引(可选)

说明:
⇒ 如果函数缺省,框架默认的键值生成函数为
(item: T, index: number) => { return index + ‘__’ + JSON.stringify(item); }
⇒ 键值生成函数不应改变任何组件状态。
@Component
struct ForEachTest {

  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  
  build() {
    List({ space: 20, initialIndex: 0 }) {
      ForEach(this.arr, (item) => {
        ListItem() {
          Text('' + item)
            .width('100%').height(100).fontSize(16)
            .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
        }
      }, item => item)
    }
    .scrollBar(BarState.Auto)
    .backgroundColor('#FFF1F3F5')
  }
}

运行效果:

在这里插入图片描述

二、键值生成规则

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

ForEach 提供了一个名为 keyGenerator 的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义 keyGenerator 函数,则 ArkUI 框架会使用默认的键值生成函数,即:

(item: any, index: number) => { return index + ‘__’ + JSON.stringify(item); }

ArkUI 框架对于 ForEach 的键值生成有一套特定的判断规则,这主要与 itemGenerator 函数的第二个参数 index 以及keyGenerator 函数的返回值有关。总的来说,只有当开发者在 itemGenerator 函数中声明了 index 参数,并且自定义的keyGenerator 函数返回值中不包含 index 参数时,ArkUI 框架才会在开发者自定义的 keyGenerator 函数返回值前添加 index 参数,作为最终的键值。在其他情况下,系统将直接使用开发者自定义的 keyGenerator 函数返回值作为最终的键值。如果 keyGenerator 函数未定义,系统将使用上述默认的键值生成函数。具体的键值生成规则判断逻辑如下图所示。

在这里插入图片描述

三、组件创建规则

在确定键值生成规则后,ForEach 的第二个参数 itemGenerator 函数会根据键值生成规则为数据源的每个数组项创建组件。

组件的创建包括两种情况:ForEach 首次渲染ForEach 非首次渲染

3.1 首次渲染

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

@Entry
@Component
struct ListTest {
  @State simpleList: Array<string> = ['First', 'Second', 'Third'];

  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)
  }

运行效果:
在这里插入图片描述

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

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

@Entry
@Component
struct ListTest {
  @State simpleList: Array<string> = ['First', 'Second', 'Second', 'Third'];

  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)
  }
}

运行效果:

在这里插入图片描述

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

3.2 非首次渲染

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

@Entry
@Component
struct ListTest {
  @State simpleList: Array<string> = ['First', 'Second', 'Third'];

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

        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
            .margin({ top: 20 })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  @Prop item: string;

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

运行效果:

在这里插入图片描述

从本例可以看出 @State 能够监听到简单数据类型数组数据源 simpleList 数组项的变化。

  1. 当 simpleList 数组项发生变化时,会触发 ForEach 进行重新渲染。
  2. ForEach 遍历新的数据源 [‘First’, ‘Second’, ‘new Third’],并生成对应的键值 FirstSecondnew Third
  3. 其中,键值 FirstSecond 在上次渲染中已经存在,所以 ForEach 复用了对应的组件并进行了渲染。对于第三个数组项 “new Third”,由于其通过键值生成规则 item 生成的键值 new Third 在上次渲染中不存在,因此 ForEach 为该数组项创建了一个新的组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值