1# 循环渲染 2 3开发框架提供循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件。ForEach定义如下: 4 5 6``` 7ForEach( 8 arr: any[], // Array to be iterated 9 itemGenerator: (item: any, index?: number) => void, // child component generator 10 keyGenerator?: (item: any, index?: number) => string // (optional) Unique key generator, which is recommended. 11) 12``` 13 14 15## ForEach 16 17 18ForEach(arr: any[],itemGenerator: (item: any, index?: number) => void, keyGenerator?: (item: any, index?: number) => string):void 19 20 21表1 参数说明 22 23| 参数名 | 参数类型 | 必填 | 默认值 | 参数描述 | 24| -------- | -------- | -------- | -------- | -------- | 25| arr | any[] | 是 | - | 必须是数组,允许空数组,空数组场景下不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。 | 26| itemGenerator | (item: any, index?: number) => void | 是 | - | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 | 27| keyGenerator | (item: any, index?: number) => string | 否 | - | 匿名参数,用于给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。 | 28 29 30>  **说明:** 31> - 必须在容器组件内使用; 32> 33> - 生成的子组件允许在ForEach的父容器组件中,允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中; 34> 35> - 子项生成器函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作: 36> ``` 37> ForEach(anArray, item => {Text(`${++counter}. item.label`)}) 38> ``` 39> 40> 正确的示例如下: 41> 42> ``` 43> ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }), 44> item => Text(`${item.i}. item.data.label`), 45> item => item.data.id.toString()) 46> ``` 47 48 49## 示例 50 51简单类型数组示例: 52 53``` 54@Entry 55@Component 56struct MyComponent { 57 @State arr: number[] = [10, 20, 30] 58 build() { 59 Column() { 60 Button() { 61 Text('Reverse Array') 62 }.onClick(() => { 63 this.arr.reverse() 64 }) 65 66 ForEach(this.arr, // Parameter 1: array to be iterated 67 (item: number) => { // Parameter 2: item generator 68 Text(`item value: ${item}`) 69 Divider() 70 }, 71 (item: number) => item.toString() // Parameter 3: unique key generator, which is optional but recommended. 72 ) 73 } 74 } 75} 76``` 77 78复杂类型数组示例: 79``` 80class Month { 81 year: number 82 month: number 83 days: Array<number> 84 85 constructor(year, month, days) { 86 this.year = year; 87 this.month = month; 88 this.days = days; 89 } 90} 91 92@Entry 93@Component 94struct Calendar1 { 95// simulate with 6 months 96 @State calendar: Month[] = [ 97 new Month(2020, 1, [...Array(31).keys()]), 98 new Month(2020, 2, [...Array(28).keys()]), 99 new Month(2020, 3, [...Array(31).keys()]), 100 new Month(2020, 4, [...Array(30).keys()]), 101 new Month(2020, 5, [...Array(31).keys()]), 102 new Month(2020, 6, [...Array(30).keys()]), 103 ] 104 105 build() { 106 Column() { 107 Button('next month') 108 .onClick(() => { 109 this.calendar.shift() 110 this.calendar.push({ 111 year: 2020, 112 month: 7, 113 days: [...Array(31) 114 .keys()] 115 }) 116 }) 117 ForEach(this.calendar, 118 (item: Month) => { 119 Text('month:' + item.month) 120 .fontSize(30) 121 .padding(20) 122 Grid() { 123 ForEach(item.days, 124 (day: number) => { 125 GridItem() { 126 Text((day + 1).toString()) 127 .fontSize(30) 128 } 129 }, 130 (day: number) => day.toString()) 131 } 132 .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr') 133 .rowsGap(20) 134 }, 135 // field is used together with year and month as the unique ID of the month. 136 (item: Month) => (item.year * 12 + item.month).toString()) 137 } 138 } 139} 140``` 141