这一节,我们来观察 ForEach 内部是如何运作的。理解这个非常重要!!!希望大家能够掌握。
首先,定义一个数据类 Article,里面只有一个属性 title。
/**
* 文章
*/
class Article {
/**
* 标题
*/
title: string
constructor(title: string) {
this.title = title
}
}
然后,使用 @Observed 装饰 Article 类,因为待会要修改 Article 内部数据,如果不使用 @Observed 进行装饰,数据源无法感知它内部被修改,UI 也就无法被刷新。
/**
* 文章
*/
@Observed
class Article {
/**
* 标题
*/
title: string
constructor(title: string) {
this.title = title
}
}
接着,定义展示数组元素的子组件 Item。
其中,article 是一个被 @ObjectLink 装饰的状态变量,类型为 Article。
重写 aboutToAppear 生命周期函数,每当组件被创建时调用。
在 build 函数中使用 Text 展示文章标题,并添加点击事件,点击时修改文本标题,观察 ForEach 的变化。
@Component
struct Item {
@ObjectLink article: Article
aboutToAppear() {
console.log(`组件已创建:${this.article.title}`)
}
build() {
Text(this.article.title)
.fontSize(30)
.margin(10)
.onClick(() => {
this.article.title = "子组件修改"
})
}
}
接下来,编写 Index 组件。
定义一个 @State 装饰的状态变量 articles 数组,并初始化它。
@Entry
@Component
struct Index {
/**
* 姓名列表
*/
@State articles: Article[] = [
new Article("javascript"),
new Article("typescript"),
new Article("harmonyos"),
]
}
最后,在 build 函数里:
- 添加一个 Button,点击时修改第一项数据。
- 使用 ForEach 遍历 articles 数组。
- 自定义 ForEach 的第三个参数 keyGenerator 函数,键值生成规则为 index + "_" + JSON.stringify(item),在函数内打印日志。
@Entry
@Component
struct Index {
...
build() {
Column() {
Button("修改")
.onClick(() => {
this.articles[0] = new Article("按钮修改")
})
ForEach(this.articles, (item: Article) => {
Item({ article: item })
}, (item: Article, index: number) => {
const result = index + "_" + JSON.stringify(item)
console.log(result)
return result
})
}
.width('100%')
}
}
完整代码:
/**
* 文章
*/
@Observed
class Article {
/**
* 标题
*/
title: string
constructor(title: string) {
this.title = title
}
}
@Entry
@Component
struct Index {
/**
* 姓名列表
*/
@State articles: Article[] = [
new Article("javascript"),
new Article("typescript"),
new Article("harmonyos"),
]
build() {
Column() {
Button("修改")
.onClick(() => {
this.articles[0] = new Article("按钮修改")
})
ForEach(this.articles, (item: Article) => {
Item({ article: item })
}, (item: Article, index: number) => {
const result = index + "_" + JSON.stringify(item)
console.log(result)
return result
})
}
.width('100%')
}
}
@Component
struct Item {
@ObjectLink article: Article
aboutToAppear() {
console.log(`组件已创建:${this.article.title}`)
}
build() {
Text(this.article.title)
.fontSize(30)
.margin(10)
.onClick(() => {
this.article.title = "子组件修改"
})
}
}
运行结果:
控制台输出:
0_{"title":"javascript"}
1_{"title":"typescript"}
2_{"title":"harmonyos"}
组件已创建:javascript
组件已创建:typescript
组件已创建:harmonyos
从运行结果来看,ForEach 首次运行做了两件事:
- 为数组中所有元素生成唯一键值。
- 为数组中所有元素创建组件。
接下来,点击其中一个子项,例如“javascript”。
控制台:
从运行结果来看,修改对象属性时,刷新UI(“javascript”变成了“子组件修改”),但 ForEach 无任何变化,即没有重新生成键值,也没有重新生成组件。
最后,我们点击修改按钮看看。
控制台:
0_{"title":"按钮修改"}
1_{"title":"typescript"}
2_{"title":"harmonyos"}
组件已创建:按钮修改
从运行结果来看,程序发生了两件事:
- ForEach 为所有元素重新生成键值。
- ForEach 为变化的元素创建新组件。
综上所述:
ForEach 在首次渲染时:
- 为所有元素创建键值。
- 为所有元素创建组件。
在非首次渲染时:
- 为所有元素重新创建键值。
- 为变化的元素创建新组件。
本文暂时没有评论,来添加一个吧(●'◡'●)