news 2026/6/10 23:15:31

HarmonyOS 教学实战(三):列表分页、下拉刷新与性能优化(让列表真正“丝滑”)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS 教学实战(三):列表分页、下拉刷新与性能优化(让列表真正“丝滑”)

在上一篇《HarmonyOS 教学实战(二)》中,我们已经让应用具备了:

  • 网络请求

  • 本地缓存

  • 离线兜底

  • 完整 Store 设计

但只要你的数据一多,列表就一定是性能重灾区

真实项目中,80% 的卡顿都来自:

❌ 列表一次性加载
❌ 下拉刷新逻辑混乱
❌ 滚动时频繁重建组件

这一篇,我们专门解决这些问题。


一、这一篇我们要解决哪些真实痛点?

围绕一个真实列表页面,完成 3 件事:

  1. 📄 列表分页加载(上拉加载更多)

  2. 🔄 下拉刷新(重新请求第一页)

  3. 🚀 列表性能优化(可复用组件 + 状态收敛)

完成后,你的列表会具备:

数据多也不卡、刷新快、滚动顺


二、先改接口:支持分页返回

假设后端接口升级为:

GET /api/tasks?page=1&pageSize=10

返回格式:

{ "list": [ { "id": 101, "title": "学习 HarmonyOS" } ], "hasMore": true }

三、第一步:升级网络 Service(分页支持)

修改service/TaskService.ets

export interface PageResult<T> { list: T[] hasMore: boolean } export class TaskService { static async fetchTasks( page: number, pageSize: number ): Promise<PageResult<Task>> { const httpRequest = http.createHttp() const response = await httpRequest.request( `https://example.com/api/tasks?page=${page}&pageSize=${pageSize}`, { method: http.RequestMethod.GET } ) const data = JSON.parse(response.result as string) return { list: data.list.map(item => new Task(item.id, item.title)), hasMore: data.hasMore } } }

四、第二步:升级 Store(分页核心逻辑)

分页的核心逻辑必须放在 Store,而不是 UI。

修改model/TaskModel.ets

@ObservedV2 export class TaskStore { tasks: Task[] = [] page: number = 1 pageSize: number = 10 hasMore: boolean = true loading: boolean = false refreshing: boolean = false async refresh() { if (this.refreshing) return this.refreshing = true this.page = 1 const result = await TaskService.fetchTasks(this.page, this.pageSize) this.tasks = result.list this.hasMore = result.hasMore this.refreshing = false } async loadMore() { if (!this.hasMore || this.loading) return this.loading = true this.page++ const result = await TaskService.fetchTasks(this.page, this.pageSize) this.tasks = this.tasks.concat(result.list) this.hasMore = result.hasMore this.loading = false } }

教学重点

  • refresh():只管第一页

  • loadMore():只管追加

  • UI 只需要调用,不参与逻辑判断


五、第三步:页面中接入下拉刷新 & 上拉加载

修改pages/Index.ets

@ComponentV2 struct Index { @Local store = new TaskStore() @Once async init() { await this.store.refresh() } build() { Column() { Refresh({ refreshing: this.store.refreshing }) { List() { ForEach(this.store.tasks, (item: Task) => { TaskItem({ task: item, onDelete: () => {} }) }, item => item.id.toString()) if (this.store.hasMore) { ListItem() { Text(this.store.loading ? '加载中...' : '上拉加载更多') .onAppear(() => this.store.loadMore()) } } } } } } }

📌onAppear是分页加载的关键
📌 不需要监听滚动位置
📌 非常稳定


六、第四步:列表性能优化(重点)

1️⃣ 使用 @ReusableV2 优化列表项

@ReusableV2 @ComponentV2 export struct TaskItem { @Param task: Task @Event onDelete: (id: number) => void build() { Row() { Text(this.task.title) Button("删除") .onClick(() => this.onDelete(this.task.id)) } } }

📌 ArkUI 会自动复用组件结构
📌 滚动时不会频繁销毁/重建


2️⃣ 避免 ListItem 中使用 @Local 状态

❌ 错误示例:

@Local checked = false

会导致每个 Item 都维护自己的状态,极易卡顿。

✔ 正确做法:
状态上移到 Store 或 Model


3️⃣ 控制刷新粒度(状态收敛)

避免这样的写法:

@Local store = new TaskStore() // 整个 store 变 → 全列表刷新

更优方式是:

  • 列表只依赖tasks

  • loading 状态放在 footer


七、第五步:缓存 + 分页怎么配合?

推荐策略:

场景行为
首次进入读缓存 → 再 refresh
下拉刷新丢弃缓存
上拉加载不写缓存
退出页面保存当前列表

📌 不要每一页都缓存
📌 缓存的是“可用数据”,不是“中间态”


八、常见分页性能坑(必看)

❌ 坑 1:List + Column 嵌套

会导致全部子项提前创建

✔ 用 List + ListItem


❌ 坑 2:key 不稳定

key = index // 非常危险

✔ 使用业务 id


❌ 坑 3:刷新 & 加载同时进行

✔ Store 中用标志位严格限制


九、现在你的 App 已经是“工业级列表”了

你已经具备:

✔ 分页加载
✔ 下拉刷新
✔ 状态隔离
✔ 高性能列表
✔ 可扩展 Store 结构

这已经是90% 商业 App 列表的实现水平

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 13:23:34

如何在M1 Mac上5分钟搭建Android环境:新手完整教程

如何在M1 Mac上5分钟搭建Android环境&#xff1a;新手完整教程 【免费下载链接】android-emulator-m1-preview 项目地址: https://gitcode.com/gh_mirrors/an/android-emulator-m1-preview 还在为Apple Silicon芯片无法运行安卓应用而烦恼吗&#xff1f;M1芯片的革命性…

作者头像 李华
网站建设 2026/6/10 16:34:51

Docker Registry 镜像缓存与客户端无感加速(以 Docker Hub 为例)

#作者&#xff1a;西门吹雪 文章目录 摘要架构与原理准备与配置Registry 配置文件&#xff08;示例&#xff09;Docker Compose 部署&#xff08;推荐&#xff09; 客户端无感化配置Docker 引擎配置&#xff08;registry‑mirrors&#xff09;无感化行为说明 使用与验证健康检…

作者头像 李华
网站建设 2026/6/10 2:58:48

如何训练自定义情感模型并集成进EmotiVoice?进阶教程发布

如何训练自定义情感模型并集成进EmotiVoice&#xff1f;进阶教程发布 在虚拟偶像直播中突然“破防”大哭&#xff0c;或是客服语音从冷静转为温柔安慰——这些细腻的情绪转折背后&#xff0c;正依赖于新一代情感化语音合成技术。传统TTS系统输出的语音往往像戴着面具说话&#…

作者头像 李华
网站建设 2026/6/7 11:15:20

Danmaku2ASS弹幕转换实战指南:从入门到精通的7大技巧

项目速览&#xff1a;弹幕转换的利器 【免费下载链接】danmaku2ass Convert comments from Niconico/AcFun/bilibili to ASS format 项目地址: https://gitcode.com/gh_mirrors/da/danmaku2ass Danmaku2ASS是一款专为弹幕爱好者设计的开源工具&#xff0c;能够将来自Nic…

作者头像 李华
网站建设 2026/6/10 15:55:29

如何完美配置ESP32-S3双SPI总线:实战终极指南

如何完美配置ESP32-S3双SPI总线&#xff1a;实战终极指南 【免费下载链接】arduino-esp32 Arduino core for the ESP32 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 你是否在ESP32-S3项目中遇到过TFT屏幕闪烁、SD卡读写失败&#xff0c;或者多个SP…

作者头像 李华
网站建设 2026/6/10 15:50:22

为什么运维转行会首选网安?网安到底好在哪?

运维工程师转网安&#xff1a;要学什么&#xff1f;有什么好处&#xff1f; 在IT圈&#xff0c;有个很有意思的现象&#xff1a;不少运维工程师干着干着&#xff0c;就一头扎进了网络安全的“坑”里。有人说这是“自卷”&#xff0c;也有人说这是“顺势而为”。今天就和大家聊聊…

作者头像 李华