HarmonyOS 小游戏《对战五子棋》开发第35篇 - 列表渲染性能优化
📅 2026/7/12 1:25:13
👁️ 阅读次数
📝 编程学习
正确的key让列表渲染更快更稳定
ForEach基本用法
ForEach(this.levels,(item:LevelConfig)=>{this.levelCard(item)},(item:LevelConfig)=>item.level.toString())三个参数:
- 数据源:
this.levels - itemGenerator:
(item) => { 渲染UI } - keyGenerator:
(item) => item.level.toString()— 返回唯一key
keyGenerator的作用
ArkUI的ForEach通过key判断哪些列表项需要更新:
数据变化前:[{level:1}, {level:2}, {level:3}] key列表: ["1", "2", "3"] 删除第2项后:[{level:1}, {level:3}] key列表: ["1", "3"] ArkUI对比key: "1" → 保留(不重绘) "2" → 不存在 → 销毁 "3" → 保留(不重绘) 结果:只销毁了第2项,第1和第3项保持不变没有keyGenerator的情况
默认使用数组索引作为key: 数据变化前:[{level:1}, {level:2}, {level:3}] key列表: ["0", "1", "2"] 删除第2项后:[{level:1}, {level:3}] key列表: ["0", "1"] ArkUI对比: "0" → 保留(但内容从level:1变成level:1)→ 不变 "1" → 保留(但内容从level:2变成level:3)→ 重绘! "2" → 不存在 → 销毁 结果:第2项被重绘(浪费),且可能闪烁本项目的keyGenerator
(item:LevelConfig)=>item.level.toString()使用关卡编号的字符串作为key——每个关卡的level是唯一的(1/2/3),保证稳定。
ForEach在关卡列表中的应用
@BuilderlevelSelectView(){Column({space:16}){Text('选择关卡').fontSize(18).fontColor('#666666').margin({top:20,bottom:8})ForEach(this.levels,(item:LevelConfig)=>{this.levelCard(item)},(item:LevelConfig)=>item.level.toString())Text(`已解锁${this.unlockedLevel}/3 关`).fontSize(13).fontColor('#999999').margin({top:8})}.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)}渲染流程:
- ForEach遍历
this.levels数组 - 对每个item调用keyGenerator生成key
- 对每个item调用itemGenerator渲染
levelCard - 当levels变化时,通过key差异决定更新策略
keyGenerator的最佳实践
好的key
// 唯一且稳定的ID(item:User)=>item.id.toString()(item:LevelConfig)=>item.level.toString()(item:Product)=>product.sku差的key
// 数组索引(默认)— 在增删时不稳定(item:any,index:number)=>index.toString()// 可能重复的值(item:User)=>item.name// 可能重名// 会变化的值(item:Product)=>item.price.toString()// 价格变了key就变了ForEach的更新机制
状态变化触发ForEach重新评估 ↓ 生成新key列表 ↓ 与旧key列表对比 ├─ 旧key不在新列表 → 销毁该项组件 ├─ 新key不在旧列表 → 创建新组件 └─ key相同 → 保留组件,更新@Prop本项目的列表变化场景
关卡列表this.levels是静态的——不会增删。但unlockedLevel变化时,ForEach不会重新渲染整个列表,只会更新卡片的enabled和opacity属性:
.enabled(item.level<=this.unlockedLevel).opacity(item.level<=this.unlockedLevel?1.0:0.5)因为key(level.toString())不变,ArkUI只更新属性,不重建组件。
ForEach与其他循环的对比
ArkUI中没有v-for或map的等价物——ForEach是唯一的列表渲染方式:
// ✅ 正确:使用ForEachForEach(this.levels,(item:LevelConfig)=>{this.levelCard(item)},(item:LevelConfig)=>item.level.toString())// ❌ 错误:不能用mapthis.levels.map((item:LevelConfig)=>{this.levelCard(item)// build()中不能使用map})总结
ForEach是ArkUI列表渲染的核心:
- 三个参数:数据源、itemGenerator、keyGenerator
- key的重要性:稳定唯一的key避免不必要的重绘
- 差异更新:只更新变化的项,提升性能
- 推荐key:业务唯一ID > 数组索引
- 唯一方式:ForEach是ArkUI中唯一的列表渲染方法
正确的keyGenerator不仅能提升性能,还能避免列表更新时的UI闪烁和状态错乱。
编程学习
技术分享
实战经验