鸿蒙HarmonyOS 5.0开辟:ForEach:循环渲染

[复制链接]
发表于 2025-11-7 09:33:49 | 显示全部楼层 |阅读模式
 往期鸿蒙全套实战文章必看:(文中附带鸿蒙全栈学习资料)



  • 鸿蒙开辟核心知识点,看这篇文章就够了
  • 最新版!鸿蒙HarmonyOS Next应用开辟实战学习蹊径
  • 鸿蒙HarmonyOS NEXT开辟技能最全学习蹊径指南
  • 鸿蒙应用开辟实战项目,看这一篇文章就够了(部门项目附源码)

ForEach:循环渲染

ForEach接口基于数组范例数据来举行循环渲染,须要与容器组件共同利用,且接口返回的组件应当是允许包罗在ForEach父容器组件中的子组件。
   分析
  从API version 9开始,该接口支持在ArkTS卡片中利用。
  键值天生规则

在ForEach循环渲染过程中,体系会为每个数组元素天生一个唯一且长期的键值,用于标识对应的组件。当这个键值变革时,ArkUI框架将视为该数组元素已被更换或修改,并会基于新的键值创建一个新的组件。
ForEach提供了一个名为keyGenerator的参数,这是一个函数,开辟者可以通过它自界说键值的天生规则。假如开辟者没有界说keyGenerator函数,则ArkUI框架会利用默认的键值天生函数,即(item: Object, index: number) => { return index + '__' + JSON.stringify(item); }。
ArkUI框架对于ForEach的键值天生有一套特定的判定规则,这重要与itemGenerator函数的第二个参数index以及keyGenerator函数的第二个参数index有关,具体的键值天生规则判定逻辑如下图所示。
图1 ForEach键值天生规则


   分析
  ArkUI框架会对重复的键值发出告诫。在UI更新的场景下,假如出现重复的键值,框架大概无法正常工作。
  组件创建规则

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

在ForEach初次渲染时,会根据前述键值天生规则为数据源的每个数组项天生唯一键值,并创建相应的组件。
  1. @Entry
  2. @Component
  3. struct Parent {
  4.   @State simpleList: Array<string> = ['one', 'two', 'three'];
  5.   build() {
  6.     Row() {
  7.       Column() {
  8.         ForEach(this.simpleList, (item: string) => {
  9.           ChildItem({ item: item })
  10.         }, (item: string) => item)
  11.       }
  12.       .width('100%')
  13.       .height('100%')
  14.     }
  15.     .height('100%')
  16.     .backgroundColor(0xF1F3F5)
  17.   }
  18. }
  19. @Component
  20. struct ChildItem {
  21.   @Prop item: string;
  22.   build() {
  23.     Text(this.item)
  24.       .fontSize(50)
  25.   }
  26. }
复制代码
运行结果如下图所示。
图2 ForEach数据源不存在雷同值案例初次渲染运行结果图


在上述代码中,键值天生规则是keyGenerator函数的返回值item。在ForEach渲染循环时,为数据源数组项依次天生键值one、two和three,并创建对应的ChildItem组件渲染到界面上。
当差别数组项按照键值天生规则天生的键值雷同时,框架的举动是未界说的。比方,在以下代码中,ForEach渲染雷同的数据项two时,只创建了一个ChildItem组件,而没有创建多个具有雷同键值的组件。
  1. @Entry
  2. @Component
  3. struct Parent {
  4.   @State simpleList: Array<string> = ['one', 'two', 'two', 'three'];
  5.   build() {
  6.     Row() {
  7.       Column() {
  8.         ForEach(this.simpleList, (item: string) => {
  9.           ChildItem({ item: item })
  10.         }, (item: string) => item)
  11.       }
  12.       .width('100%')
  13.       .height('100%')
  14.     }
  15.     .height('100%')
  16.     .backgroundColor(0xF1F3F5)
  17.   }
  18. }
  19. @Component
  20. struct ChildItem {
  21.   @Prop item: string;
  22.   build() {
  23.     Text(this.item)
  24.       .fontSize(50)
  25.   }
  26. }
复制代码
运行结果如下图所示。
图3 ForEach数据源存在雷同值案例初次渲染运行结果图


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

在ForEach组件举行非初次渲染时,它会查抄新天生的键值是否在前次渲染中已经存在。假如键值不存在,则会创建一个新的组件;假如键值存在,则不会创建新的组件,而是直接渲染该键值所对应的组件。比方,在以下的代码示例中,通过点击变乱修改了数组的第三项值为"new three",这将触发ForEach组件举行非初次渲染。
  1. @Entry
  2. @Component
  3. struct Parent {
  4.   @State simpleList: Array<string> = ['one', 'two', 'three'];
  5.   build() {
  6.     Row() {
  7.       Column() {
  8.         Text('点击修改第3个数组项的值')
  9.           .fontSize(24)
  10.           .fontColor(Color.Red)
  11.           .onClick(() => {
  12.             this.simpleList[2] = 'new three';
  13.           })
  14.         ForEach(this.simpleList, (item: string) => {
  15.           ChildItem({ item: item })
  16.             .margin({ top: 20 })
  17.         }, (item: string) => item)
  18.       }
  19.       .justifyContent(FlexAlign.Center)
  20.       .width('100%')
  21.       .height('100%')
  22.     }
  23.     .height('100%')
  24.     .backgroundColor(0xF1F3F5)
  25.   }
  26. }
  27. @Component
  28. struct ChildItem {
  29.   @Prop item: string;
  30.   build() {
  31.     Text(this.item)
  32.       .fontSize(30)
  33.   }
  34. }
复制代码
运行结果如下图所示。
图4 ForEach非初次渲染案例运行结果图

从本例可以看出@State 可以大概监听到简单数据范例数组数据源 simpleList 数组项的变革。

  • 当 simpleList 数组项发生变革时,会触发 ForEach 举行重新渲染。
  • ForEach 遍历新的数据源 ['one', 'two', 'new three'],并天生对应的键值one、two和new three。
  • 此中,键值one和two在前次渲染中已经存在,以是 ForEach 复用了对应的组件并举行了渲染。对于第三个数组项 "new three",由于其通过键值天生规则 item 天生的键值new three在前次渲染中不存在,因此 ForEach 为该数组项创建了一个新的组件。
利用场景

ForEach组件在开辟过程中的重要应用场景包罗:数据源稳定、数据源数组项发生变革(如插入、删除操纵)、数据源数组项子属性变革。
数据源稳定

在数据源保持稳定的场景中,数据源可以直接接纳根本数据范例。比方,在页面加载状态时,可以利用骨架屏列表举行渲染展示。
  1. @Entry
  2. @Component
  3. struct ArticleList {
  4.   @State simpleList: Array<number> = [1, 2, 3, 4, 5];
  5.   build() {
  6.     Column() {
  7.       ForEach(this.simpleList, (item: number) => {
  8.         ArticleSkeletonView()
  9.           .margin({ top: 20 })
  10.       }, (item: number) => item.toString())
  11.     }
  12.     .padding(20)
  13.     .width('100%')
  14.     .height('100%')
  15.   }
  16. }
  17. @Builder
  18. function textArea(width: number | Resource | string = '100%', height: number | Resource | string = '100%') {
  19.   Row()
  20.     .width(width)
  21.     .height(height)
  22.     .backgroundColor('#FFF2F3F4')
  23. }
  24. @Component
  25. struct ArticleSkeletonView {
  26.   build() {
  27.     Row() {
  28.       Column() {
  29.         textArea(80, 80)
  30.       }
  31.       .margin({ right: 20 })
  32.       Column() {
  33.         textArea('60%', 20)
  34.         textArea('50%', 20)
  35.       }
  36.       .alignItems(HorizontalAlign.Start)
  37.       .justifyContent(FlexAlign.SpaceAround)
  38.       .height('100%')
  39.     }
  40.     .padding(20)
  41.     .borderRadius(12)
  42.     .backgroundColor('#FFECECEC')
  43.     .height(120)
  44.     .width('100%')
  45.     .justifyContent(FlexAlign.SpaceBetween)
  46.   }
  47. }
复制代码
运行结果如下图所示。
图5 骨架屏运行结果图


在本示例中,接纳数据项item作为键值天生规则,由于数据源simpleList的数组项各不雷同,因此可以大概包管键值的唯一性。
数据源数组项发生变革

在数据源数组项发生变革的场景下,比方举行数组插入、删除操纵大概数组项索引位置发生交换时,数据源应为对象数组范例,并利用对象的唯一ID作为终极键值。比方,当在页面上通过手势上滑加载下一页数据时,会在数据源数组尾部新增新获取的数据项,从而使得数据源数组长度增大。
  1. class Article {
  2.   id: string;
  3.   title: string;
  4.   brief: string;
  5.   constructor(id: string, title: string, brief: string) {
  6.     this.id = id;
  7.     this.title = title;
  8.     this.brief = brief;
  9.   }
  10. }
  11. @Entry
  12. @Component
  13. struct ArticleListView {
  14.   @State isListReachEnd: boolean = false;
  15.   @State articleList: Array<Article> = [
  16.     new Article('001', '第1篇文章', '文章简介内容'),
  17.     new Article('002', '第2篇文章', '文章简介内容'),
  18.     new Article('003', '第3篇文章', '文章简介内容'),
  19.     new Article('004', '第4篇文章', '文章简介内容'),
  20.     new Article('005', '第5篇文章', '文章简介内容'),
  21.     new Article('006', '第6篇文章', '文章简介内容')
  22.   ]
  23.   loadMoreArticles() {
  24.     this.articleList.push(new Article('007', '加载的新文章', '文章简介内容'));
  25.   }
  26.   build() {
  27.     Column({ space: 5 }) {
  28.       List() {
  29.         ForEach(this.articleList, (item: Article) => {
  30.           ListItem() {
  31.             ArticleCard({ article: item })
  32.               .margin({ top: 20 })
  33.           }
  34.         }, (item: Article) => item.id)
  35.       }
  36.       .onReachEnd(() => {
  37.         this.isListReachEnd = true;
  38.       })
  39.       .parallelGesture(
  40.         PanGesture({ direction: PanDirection.Up, distance: 80 })
  41.           .onActionStart(() => {
  42.             if (this.isListReachEnd) {
  43.               this.loadMoreArticles();
  44.               this.isListReachEnd = false;
  45.             }
  46.           })
  47.       )
  48.       .padding(20)
  49.       .scrollBar(BarState.Off)
  50.     }
  51.     .width('100%')
  52.     .height('100%')
  53.     .backgroundColor(0xF1F3F5)
  54.   }
  55. }
  56. @Component
  57. struct ArticleCard {
  58.   @Prop article: Article;
  59.   build() {
  60.     Row() {
  61.       // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
  62.       Image($r('app.media.icon'))
  63.         .width(80)
  64.         .height(80)
  65.         .margin({ right: 20 })
  66.       Column() {
  67.         Text(this.article.title)
  68.           .fontSize(20)
  69.           .margin({ bottom: 8 })
  70.         Text(this.article.brief)
  71.           .fontSize(16)
  72.           .fontColor(Color.Gray)
  73.           .margin({ bottom: 8 })
  74.       }
  75.       .alignItems(HorizontalAlign.Start)
  76.       .width('80%')
  77.       .height('100%')
  78.     }
  79.     .padding(20)
  80.     .borderRadius(12)
  81.     .backgroundColor('#FFECECEC')
  82.     .height(120)
  83.     .width('100%')
  84.     .justifyContent(FlexAlign.SpaceBetween)
  85.   }
  86. }
复制代码
初始运行结果(左图)和手势上滑加载后结果(右图)如下图所示。
图6 数据源数组项变革案例运行结果图


在本示例中,ArticleCard组件作为ArticleListView组件的子组件,通过@Prop装饰器吸取一个Article对象,用于渲染文章卡片。

  • 当列表滚动到底部时,假如手势滑动隔断凌驾指定的80,将触发loadMoreArticle()函数。此函数会在articleList数据源的尾部添加一个新的数据项,从而增长数据源的长度。
  • 数据源被@State装饰器修饰,ArkUI框架可以大概感知到数据源长度的变革,并触发ForEach举行重新渲染。
数据源数组项子属性变革

当数据源的数组项为对象数据范例,而且只修改某个数组项的属性值时,由于数据源为复杂数据范例,ArkUI框架无法监听到@State装饰器修饰的数据源数组项的属性变革,从而无法触发ForEach的重新渲染。为实现ForEach重新渲染,须要团结@Observed和@ObjectLink装饰器利用。比方,在文章列表卡片上点击“点赞”按钮,从而修改文章的点赞数目。
  1. @Observed
  2. class Article {
  3.   id: string;
  4.   title: string;
  5.   brief: string;
  6.   isLiked: boolean;
  7.   likesCount: number;
  8.   constructor(id: string, title: string, brief: string, isLiked: boolean, likesCount: number) {
  9.     this.id = id;
  10.     this.title = title;
  11.     this.brief = brief;
  12.     this.isLiked = isLiked;
  13.     this.likesCount = likesCount;
  14.   }
  15. }
  16. @Entry
  17. @Component
  18. struct ArticleListView {
  19.   @State articleList: Array<Article> = [
  20.     new Article('001', '第0篇文章', '文章简介内容', false, 100),
  21.     new Article('002', '第1篇文章', '文章简介内容', false, 100),
  22.     new Article('003', '第2篇文章', '文章简介内容', false, 100),
  23.     new Article('004', '第4篇文章', '文章简介内容', false, 100),
  24.     new Article('005', '第5篇文章', '文章简介内容', false, 100),
  25.     new Article('006', '第6篇文章', '文章简介内容', false, 100),
  26.   ];
  27.   build() {
  28.     List() {
  29.       ForEach(this.articleList, (item: Article) => {
  30.         ListItem() {
  31.           ArticleCard({
  32.             article: item
  33.           })
  34.             .margin({ top: 20 })
  35.         }
  36.       }, (item: Article) => item.id)
  37.     }
  38.     .padding(20)
  39.     .scrollBar(BarState.Off)
  40.     .backgroundColor(0xF1F3F5)
  41.   }
  42. }
  43. @Component
  44. struct ArticleCard {
  45.   @ObjectLink article: Article;
  46.   handleLiked() {
  47.     this.article.isLiked = !this.article.isLiked;
  48.     this.article.likesCount = this.article.isLiked ? this.article.likesCount + 1 : this.article.likesCount - 1;
  49.   }
  50.   build() {
  51.     Row() {
  52.       // 此处'app.media.icon'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
  53.       Image($r('app.media.icon'))
  54.         .width(80)
  55.         .height(80)
  56.         .margin({ right: 20 })
  57.       Column() {
  58.         Text(this.article.title)
  59.           .fontSize(20)
  60.           .margin({ bottom: 8 })
  61.         Text(this.article.brief)
  62.           .fontSize(16)
  63.           .fontColor(Color.Gray)
  64.           .margin({ bottom: 8 })
  65.         Row() {
  66.           // 此处app.media.iconLiked','app.media.iconUnLiked'仅作示例,请开发者自行替换,否则imageSource创建失败会导致后续无法正常执行。
  67.           Image(this.article.isLiked ? $r('app.media.iconLiked') : $r('app.media.iconUnLiked'))
  68.             .width(24)
  69.             .height(24)
  70.             .margin({ right: 8 })
  71.           Text(this.article.likesCount.toString())
  72.             .fontSize(16)
  73.         }
  74.         .onClick(() => this.handleLiked())
  75.         .justifyContent(FlexAlign.Center)
  76.       }
  77.       .alignItems(HorizontalAlign.Start)
  78.       .width('80%')
  79.       .height('100%')
  80.     }
  81.     .padding(20)
  82.     .borderRadius(12)
  83.     .backgroundColor('#FFECECEC')
  84.     .height(120)
  85.     .width('100%')
  86.     .justifyContent(FlexAlign.SpaceBetween)
  87.   }
  88. }
复制代码
上述代码的初始运行结果(左图)和点击第1个文章卡片上的点赞图标后的运行结果(右图)如下图所示。
图7 数据源数组项子属性变革案例运行结果图


在本示例中,Article类被@Observed装饰器修饰。父组件ArticleListView传入Article对象实例给子组件ArticleCard,子组件利用@ObjectLink装饰器吸取该实例。

  • 当点击第1个文章卡片上的点赞图标时,会触发ArticleCard组件的handleLiked函数。该函数修改第1个卡片对应组件里article实例的isLiked和likesCount属性值。
  • 由于子组件ArticleCard中的article利用了@ObjectLink装饰器,父子组件共享同一份article数据。因此,父组件中articleList的第1个数组项的isLiked和likedCounts数值也会同步修改。
  • 当父组件监听到数据源数组项属性值变革时,会触发ForEach重新渲染。
  • 在此处,ForEach键值天生规则为数组项的id属性值。当ForEach遍历新数据源时,数组项的id均没有变革,不会新建组件。
  • 渲染第1个数组项对应的ArticleCard组件时,读取到的isLiked和likesCount为修改后的新值。
拖拽排序

当ForEach在List组件下利用,而且设置了onMove变乱,ForEach每次迭代都天生一个ListItem时,可以使能拖拽排序。拖拽排序离手后,假如数据位置发生变革,则会触发onMove变乱,上报数据移动原始索引号和目的索引号。在onMove变乱中,须要根据上报的起始索引号和目的索引号修改数据源。数据源修改前后,要保持每个数据的键值稳定,只是次序发生变革,才气包管落位动画正常实行。
  1. @Entry
  2. @Component
  3. struct ForEachSort {
  4.   @State arr: Array<string> = [];
  5.   build() {
  6.     Row() {
  7.       List() {
  8.         ForEach(this.arr, (item: string) => {
  9.           ListItem() {
  10.             Text(item.toString())
  11.               .fontSize(16)
  12.               .textAlign(TextAlign.Center)
  13.               .size({height: 100, width: "100%"})
  14.           }.margin(10)
  15.           .borderRadius(10)
  16.           .backgroundColor("#FFFFFFFF")
  17.         }, (item: string) => item)
  18.           .onMove((from:number, to:number) => {
  19.             let tmp = this.arr.splice(from, 1);
  20.             this.arr.splice(to, 0, tmp[0])
  21.           })
  22.       }
  23.       .width('100%')
  24.       .height('100%')
  25.       .backgroundColor("#FFDCDCDC")
  26.     }
  27.   }
  28.   aboutToAppear(): void {
  29.     for (let i = 0; i < 100; i++) {
  30.       this.arr.push(i.toString())
  31.     }
  32.   }
  33. }
复制代码
图8 ForEach拖拽排序结果图

利用发起



  • 为满意键值的唯一性,对于对象数据范例,发起利用对象数据中的唯一id作为键值。
  • 只管克制在终极的键值天生规则中包罗数据项索引index,以防止出现渲染结果非预期和渲染性能低沉。假如业务确实须要利用index,比方列表须要通过index举行条件渲染,开辟者须要担当ForEach在改变数据源后重新创建组件所带来的性能斲丧。
  • 根本数据范例的数据项没有唯一ID属性。假如利用根本数据范例自己作为键值,必须确保数组项无重复。因此,对于数据源会发生变革的场景,发起将根本数据范例数组转化为具备唯一ID属性的对象数据范例数组,再利用ID属性作为键值天生规则。
  • 对于以上限定规则,index参数存在的意义为:index是开辟者包管键值唯一性的终极本领;对数据项举行修改时,由于itemGenerator中的item参数是不可修改的,以是须用index索引值对数据源举行修改,进而触发UI重新渲染。
  • ForEach在下列容器组件 List、Grid、Swiper以及WaterFlow 内利用的时间,不要与LazyForEach 混用。 以List为例,同时包罗ForEach、LazyForEach的环境是不保举的。
不保举案例

开辟者在利用ForEach的过程中,若对于键值天生规则的明白不敷充实,大概会出现错误的利用方式。错误利用一方面会导致功能层面题目,比方渲染结果非预期,另一方面会导致性能层面题目,比方渲染性能低沉。
渲染结果非预期

在本示例中,通过设置ForEach的第三个参数KeyGenerator函数,自界说键值天生规则为数据源的索引index的字符串范例值。当点击父组件Parent中“在第1项后插入新项”文本组件后,界面会出现非预期的结果。
  1. @Entry
  2. @Component
  3. struct Parent {
  4.   @State simpleList: Array<string> = ['one', 'two', 'three'];
  5.   build() {
  6.     Column() {
  7.       Button() {
  8.         Text('在第1项后插入新项').fontSize(30)
  9.       }
  10.       .onClick(() => {
  11.         this.simpleList.splice(1, 0, 'new item');
  12.       })
  13.       ForEach(this.simpleList, (item: string) => {
  14.         ChildItem({ item: item })
  15.       }, (item: string, index: number) => index.toString())
  16.     }
  17.     .justifyContent(FlexAlign.Center)
  18.     .width('100%')
  19.     .height('100%')
  20.     .backgroundColor(0xF1F3F5)
  21.   }
  22. }
  23. @Component
  24. struct ChildItem {
  25.   @Prop item: string;
  26.   build() {
  27.     Text(this.item)
  28.       .fontSize(30)
  29.   }
  30. }
复制代码
上述代码的初始渲染结果和点击“在第1项后插入新项”文本组件后的渲染结果如下图所示。
图9 渲染结果非预期运行结果图


ForEach在初次渲染时,创建的键值依次为"0"、"1"、"2"。
插入新项后,数据源simpleList变为['one', 'new item', 'two', 'three'],框架监听到@State装饰的数据源长度变革触发ForEach重新渲染。
ForEach依次遍历新数据源,遍历数据项"one"时天生键值"0",存在雷同键值,因此不创建新组件。继续遍历数据项"new item"时天生键值"1",存在雷同键值,因此不创建新组件。继续遍历数据项"two"天生键值"2",存在雷同键值,因此不创建新组件。末了遍历数据项"three"时天生键值"3",不存在雷同键值,创建内容为"three"的新组件并渲染。
从以上可以看出,当终极键值天生规则包罗index时,盼望的界面渲染结果为['one', 'new item', 'two', 'three'],而实际的渲染结果为['one', 'two', 'three', 'three'],渲染结果不符合开辟者预期。因此,开辟者在利用ForEach时应只管克制终极键值天生规则中包罗index。
渲染性能低沉

在本示例中,ForEach的第三个参数KeyGenerator函数处于缺省状态。根据上述键值天生规则,此例利用框架默认的键值天生规则,即终极键值为字符串index + '__' + JSON.stringify(item)。当点击“在第1项后插入新项”文本组件后,ForEach将须要为第2个数组项以及厥后的全部项重新创建组件。
  1. @Entry
  2. @Component
  3. struct Parent {
  4.   @State simpleList: Array<string> = ['one', 'two', 'three'];
  5.   build() {
  6.     Column() {
  7.       Button() {
  8.         Text('在第1项后插入新项').fontSize(30)
  9.       }
  10.       .onClick(() => {
  11.         this.simpleList.splice(1, 0, 'new item');
  12.         console.log(`[onClick]: simpleList is ${JSON.stringify(this.simpleList)}`);
  13.       })
  14.       ForEach(this.simpleList, (item: string) => {
  15.         ChildItem({ item: item })
  16.       })
  17.     }
  18.     .justifyContent(FlexAlign.Center)
  19.     .width('100%')
  20.     .height('100%')
  21.     .backgroundColor(0xF1F3F5)
  22.   }
  23. }
  24. @Component
  25. struct ChildItem {
  26.   @Prop item: string;
  27.   aboutToAppear() {
  28.     console.log(`[aboutToAppear]: item is ${this.item}`);
  29.   }
  30.   build() {
  31.     Text(this.item)
  32.       .fontSize(50)
  33.   }
  34. }
复制代码
以上代码的初始渲染结果和点击"在第1项后插入新项"文本组件后的渲染结果如下图所示。
图10 渲染性能低沉案例运行结果图


点击“在第1项后插入新项”文本组件后,IDE的日记打印结果如下所示。
图11 渲染性能低沉案例日记打印图


插入新项后,ForEach为new item、 two、 three三个数组项创建了对应的组件ChildItem,并实行了组件的aboutToAppear()生命周期函数。这是由于:

  • 在ForEach初次渲染时,创建的键值依次为0__one、1__two、2__three。
  • 插入新项后,数据源simpleList变为['one', 'new item', 'two', 'three'],ArkUI框架监听到@State装饰的数据源长度变革触发ForEach重新渲染。
  • ForEach依次遍历新数据源,遍历数据项one时天生键值0__one,键值已存在,因此不创建新组件。继续遍历数据项new item时天生键值1__new item,不存在雷同键值,创建内容为new item的新组件并渲染。继续遍历数据项two天生键值2__two,不存在雷同键值,创建内容为two的新组件并渲染。末了遍历数据项three时天生键值3__three,不存在雷同键值,创建内容为three的新组件并渲染。
只管此示例中界面渲染的结果符合预期,但每次插入一条新数组项时,ForEach都会为从该数组项起反面的全部数组项全部重新创建组件。当数据源数据量较大或组件结构复杂时,由于组件无法得到复用,将导致性能体验不佳。因此,除非须要,否则不保举将第三个参数KeyGenerator函数处于缺省状态,以及在键值天生规则中包罗数据项索引index。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表