LazyForEach性能优化:解决长列表卡顿问题

news/2025/9/24 16:26:44/文章来源:https://www.cnblogs.com/xpzll/p/19109469

本文将深入解析HarmonyOS中LazyForEach的工作原理、性能优势、实战优化技巧及常见问题解决方案,帮助你构建流畅的长列表体验。

1. LazyForEach 核心优势与原理

LazyForEach 是鸿蒙ArkUI框架中为高性能列表渲染设计的核心组件,其核心设计思想基于动态加载资源回收机制。与一次性加载全量数据的ForEach不同,LazyForEach仅渲染当前屏幕可视区域内的列表项及少量缓存项,从而大幅降低内存消耗,支持万级数据的流畅滚动。

1.1 与 ForEach 的关键差异

特性 LazyForEach ForEach
渲染策略 按需加载 + 节点回收 全量渲染
内存占用 动态控制(更低) 固定(更高)
适用场景 长列表/复杂项 短列表/简单项
性能优化 自动回收 + 复用 无特殊优化
数据更新 需 DataChangeListener 直接响应式更新

数据量超过100条时,LazyForEach的优势愈发明显。万条数据下,其内存占用可降低86%,首屏耗时减少77%,丢帧率从58.2%降至0%。

2. 基础用法与数据源实现

2.1 基础代码示例

// 1. 定义数据源,必须实现IDataSource接口
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];private originDataArray: string[] = [];totalCount(): number { return this.originDataArray.length; }getData(index: number): string { return this.originDataArray[index]; }registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {this.listeners.push(listener);}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) { this.listeners.splice(pos, 1); }}// 数据变更时通知监听器notifyDataReload(): void {this.listeners.forEach(listener => { listener.onDataReloaded(); })}notifyDataAdd(index: number): void {this.listeners.forEach(listener => { listener.onDataAdd(index); })}
}// 2. 在组件中使用LazyForEach
@Entry
@Component
struct PerformanceList {private data: BasicDataSource = new BasicDataSource();build() {List({ space: 10 }) {LazyForEach(this.data,(item: string) => {ListItem() {Text(item).fontSize(16)}},(item: string) => item // 唯一键生成器)}.cachedCount(3) // 设置缓存数量}
}

2.2 关键实现要点

  • 唯一键生成器:必须提供,用于组件复用时的身份标识,建议使用项的唯一ID而非索引。
  • 数据源接口:必须实现IDataSource接口的totalCount()getData()等方法。
  • 数据更新:严禁直接修改数据源数组,必须通过数据源中注册的监听器(DataChangeListener)通知变更。

3. 性能优化实战技巧

3.1 缓存策略(cachedCount)

通过cachedCount预加载屏幕外指定数量的列表项,可有效解决快速滑动时的白块问题。

List() {LazyForEach(this.dataSource, (item) => { /* ... */ })
}
.cachedCount(5) // 推荐值为屏幕可见项数的1-2倍

设置建议:一屏显示6条 → 设cachedCount=3(屏幕外缓存一半);若列表含图片/视频等大资源,可适当增大缓存(如cachedCount=6)。

3.2 组件复用(@Reusable)

使用@Reusable装饰器标记可复用组件,滑出可视区的组件会被存入复用池,需要时直接更新数据而非重新创建,显著降低组件创建时间和内存开销。

@Reusable
@Component
struct ReusableListItem {@Prop item: MyDataItem; // 使用@Prop而非@Link接收数据aboutToReuse(params: Record<string, Object>) {// 组件复用时更新数据,比重新创建快10倍!this.item = params.item as MyDataItem;}build() {Row() {Image(this.item.avatar).width(50).height(50)Text(this.item.name).fontSize(16)}}
}
// 在LazyForEach中使用
LazyForEach(this.data, (item: MyDataItem) => {ListItem() {ReusableListItem({ item }) // 传递参数}
}, (item) => item.id.toString())

3.3 布局优化

  • 减少嵌套层级:使用RelativeContainer实现扁平化布局,将所有组件置于同一层级,减少渲染计算量。
  • 慎用条件语句:避免在列表项中使用if/else控制不同布局结构,这会阻碍组件复用。可拆分为不同组件或用display属性控制显隐。

3.4 图片优化

对于网络图片,使用同步加载或预加载避免复用导致的闪烁。

@Reusable
@Component
struct StableImage {@Prop url: string;private cachedImage = new LRUCache(20); // 使用LRU缓存build() {Image(this.cachedImage.get(this.url) || fetchImage(this.url)).syncLoad(true) // 同步加载}
}

3.5 数据更新优化

使用@ObjectLink@Observed进行数据双向绑定,避免不必要的深拷贝和组件重建。

@Observed
class MyDataItem {id: string;name: string;
}@Reusable
@Component
struct MyListItem {@ObjectLink item: MyDataItem; // 使用@ObjectLink而非@Propbuild() {// ...}
}

4. 常见问题与解决方案

  1. 列表项错乱
    • 根因:键值生成规则不唯一,或使用了索引(index)作为键。
    • 解决:确保使用唯一且稳定的标识(如item.id),或采用复合键${id}_${timestamp}
  2. 图片闪烁
    • 根因:组件复用时Image重新加载。
    • 解决:使用Image.syncLoad(true)或实现图片缓存机制(如LRUCache)。
  3. 数据更新后UI“闪”或先展示旧数据
    • 根因:更新数据时改变了可视区及缓存区内组件的键值[key]。
    • 解决:更新数据时不要改变可视区及缓存区(cachedCount范围内)组件的键值。应通过@ObjectLink@Observed机制局部更新数据,或手动调用组件更新方法。
  4. 滑动卡顿
    • 排查: 检查cachedCount是否设置合理。 确认@ReusablereuseId是否正确使用。 避免在列表滑动过程中进行大量计算或耗时操作。
    • 优化:使用@Builder替代自定义组件@Component以减少嵌套和节点创建开销。

5. 总结

LazyForEach是处理HarmonyOS长列表的首选方案,通过按需加载缓存策略组件复用布局优化等手段,可显著提升性能。记住以下要点:

  • 键值唯一:确保键生成器返回稳定唯一的标识。
  • 合理缓存:设置cachedCount为可视项数量的1-2倍。
  • 组件复用:对复杂列表项使用@Reusable装饰器。
  • 数据更新:通过数据监听器通知变更,并使用@ObjectLink进行高效更新。
  • 布局扁平:减少嵌套层级,优先使用RelativeContainer

对于不足100项的短列表,使用ForEach更为简单;但对于长列表,LazyForEach是保障流畅体验的关键。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完整教程:SWR:React 数据获取的现代解决方案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Redis数据结构的最佳实践 - 公众号

本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!🚀 魔都架构师 | 全网30W技术追随者 🔧 大厂分布式系统/数据中台实战专家 🏆 主导交易系统百万级流量调优 & 车联网平台架构 🧠 AIGC应用…

PyTorch 神经网络工具箱 - 实践

PyTorch 神经网络工具箱 - 实践2025-09-24 16:21 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

java函数式编程的学习01

java函数式编程:在stream流中经常用到 对stream流的理解:操作集合的一种方法 stream流的用法:创建流、中间操作、终结操作 创建流的方式以及一些注意事项: 如果是集合通过.stream()方法来创建流,如果是数组,可以…

Manim实现镜面反射特效

本文将介绍如何使用ManimCE框架实现镜面反射特效,让你的动画更加生动有趣。 1. 实现原理 1.1. 对称点计算 实现镜面反射的核心是计算点关于直线的对称点。 代码中的symmetry_point函数通过向量投影的方法计算对称点:…

25Java基础之IO(二)

IO流-字符流 FileReader(文件字符输入流)作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。案例:读取一个字符//目标:文件字符输入流的使用,每次读取一个字符。 public class FileReaderDemo01 …

【git】统计项目下每个人提交行数

git log --format=%aN | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk { add += $1; subs += $2; loc += $1 - $2 } END { p…

【P2860】[USACO06JAN] Redundant Paths G - Harvey

题意 给定一个连通图,求最少要加多少条边使得图无割边。 思路 首先,我们可以先缩点再进行考虑。 缩点后整个连通图变成一棵树,为了使连边后不出现割边,可以将所有度为 \(1\) 的点两两连边,如果度为 \(1\) 的点的个…

GUI软件构造

GUI(桌面图形用户界面) 设计遵循规范,要标准,不繁杂 JAVA GUI设计模式 观察者模式是一种软件设计模式 ,他定义了一种一对多的依赖关系,一个对象改变其他对象自动更新 包含的角色 被观察对象(subject) 具体被观…

网站页面建设方案书模板wordpress模班之家

1. 字面含义不同 Comparable字面意思是“具有比较能力”&#xff0c;Comparator字面意思是“比较器”。 2. 用法不同 Comparable用法&#xff1a;对需要排序的类&#xff0c;实现Comparable接口&#xff0c;重写compareTo()方法。 Comparator用法&#xff1a;创建自定义比较…

ssh蒙语网站开发室内设计公司办公室图片

在孩子学习过程中&#xff0c;假设有一种“方法”&#xff0c;能让孩子成绩突飞猛进&#xff0c;你想不想掌握&#xff1f;在孩子学习过程中&#xff0c;假设有一套“系统”&#xff0c;能让孩子主动喜欢上学习&#xff0c;你想不想拥有&#xff1f;在孩子学习过程中&#xff0…

点餐网站怎么做哈尔滨网站建设制作

导读:本文主要围绕材料非线性问题的有限元Matlab编程求解进行介绍,重点围绕牛顿-拉普森法(切线刚度法)、初应力法、初应变法等三种非线性迭代方法的算法原理展开讲解,最后利用Matlab对材料非线性问题有限元迭代求解算法进行实现,展示了实现求解的核心代码。这些内容都将收…

【CV】GAN代码解析 image_folder.py

【CV】GAN代码解析 image_folder.pyPosted on 2025-09-24 16:07 SaTsuki26681534 阅读(0) 评论(0) 收藏 举报"""A modified image folder classWe modify the official PyTorch image folder (htt…

一些常用的网站

📚 我的常用网址收藏夹前言: 记录那些在我的数字生活中不可或缺的网站和工具,方便快速访问和分享。🚀 常用工具 开发与编程插件库: open-vsx - vscode/trae的历史插件下载 技术文档: MDN Web Docs - 前端开发者的…

systemd-nspawn容器体积精简和桥接网络实战

systemd-nspawn容器体积精简和桥接网络实战目录前言需求精简容器体积创建目录结构测试容器是否正常启动创建并测试容器的独立网络形成systemd服务文件。通过wifi连接网关的容器配置其他说明前言 以前我的树莓派服务是放…

运维自动化工具Ansible大总结20250914 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

贵州省住房和城乡建设部网站成都住建局官网站首页

目录 概念图遍历深度优先搜索 (DFS)DFS 适用场景DFS 优缺点 广度优先搜索 (BFS)BFS 适用场景BFS 优缺点 DFS & BFS 异同点 图搜索Dijkstra算法A*算法Floyd算法Bellman-Ford算法SPFA算法 概念 图遍历和图搜索是解决图论问题时常用的两种基本操作。 图遍历是指从图中的某一个…

上海建筑 公司网站wordpress 伪静态

科技巨变,未来已来,八大技术趋势引领数字化时代。信息技术的迅猛发展,深刻改变了我们的生活、工作和生产方式。人工智能、物联网、云计算、大数据、虚拟现实、增强现实、区块链、量子计算等新兴技术在各行各业得到广泛应用,为各个领域带来了新的活力和变革。 为了更好地了解…

杭州网站前端建设seo全称是什么意思

目录 从上到下&#xff0c;你所看到的目录如下 /bin /bin 目录是包含一些二进制文件的目录&#xff0c;即可以运行的一些应用程序。 你会在这个目录中找到上面提到的 ls 程序&#xff0c;以及用于新建和删除文件和目录、移动它们基本工具。还有其它一些程序&#xff0c;等等。…

企业微信客服API模式接入第三方客服系统,对接大模型AI智能体

我们系统可以接入企业微信客服的API gofly.v1kf.com 联系vx:llike620企业微信客服是企业微信里面的一项功能,它整合了微信生态的优势,解决的是与临时访客进行实时沟通的需求 核心功能 多渠道接待:支持在微信内(公…