鸿蒙 7 List 组件,懒加载与高性能分页实战
业务开发里几乎都会遇到长列表:商品列表、消息列表、订单列表。很多新手第一反应直接用ForEach套 Column,一次性渲染全部数据。数据少看着没问题,一旦上千条数据,页面直接卡顿、滑动掉帧、内存飙升,甚至 App 闪退。
鸿蒙 7 提供List组件,自带懒加载(按需渲染),只有进入屏幕可视区域的 Item 才会创建渲染,滑出去的 Item 会回收复用。这就是长列表首选方案。
误区:List ≠ 简单的竖向排列。普通 Column+ForEach 是一次性渲染所有节点,没有复用;List 自带 Item 复用池,大数据量下性能差距巨大。
今天我们从基础 List 写法、ListItem、列表分割线,再到懒加载原理、下拉刷新、上拉加载更多分页,全部用鸿蒙 7 可直接运行代码演示,一并讲清长列表最常见的性能坑。
一、基础 List + ListItem 最简示例
List 是容器,ListItem代表列表里面每一行条目。List 默认垂直排列。
@Entry
@Component
struct ListSimpleDemo {
// 模拟列表数据源
@State dataList: string[] = ["条目1", "条目2", "条目3", "条目4", "条目5"];
build() {
Column() {
List() {
ForEach(this.dataList, (item: string) => {
ListItem() {
Text(item)
.fontSize(18)
.padding(16)
}
}, (item: string) => item)
}
.width('100%')
.height('100%')
}
}
}
代码拆解
List:列表容器,必须设置高度(固定 / 百分比),不设置高度 List 无法滚动、懒加载失效。ListItem:列表每一行的根节点,List 内部只能放 ListItem,不能直接放 Text、Button。- ForEach 第三个参数:
key生成函数,必须提供唯一 key,用于 Item 复用,key 重复会出现 UI 错乱。
重点坑:忘记给 List 设置 height。很多新手写 List 不限制高度,List 会一次性渲染全部数据,懒加载直接失效。

二、美化列表:分割线、间距、点击事件
真实列表一般需要分割线,条目点击跳转。
@Entry
@Component
struct ListItemStyleDemo {
@State dataList: string[] = ["苹果", "香蕉", "橙子", "葡萄", "芒果"];
build() {
Column() {
List() {
ForEach(this.dataList, (item: string) => {
ListItem() {
Row() {
Text(item)
.fontSize(18)
}
.width('100%')
.padding(16)
}
.onClick(()=>{
console.info("点击了:" + item)
})
}, (item:string)=>item)
}
.width('100%')
.height('100%')
.divider({ strokeWidth:1, color:"#eee" }) // 分割线
}
}
}
.divider():快速添加列表分割线,strokeWidth 控制线粗细;- 点击事件写在 ListItem 上,整行可点击。

三、上拉加载更多(分页加载)
业务最常用场景:滚动到底部,请求下一页数据。 List 提供onScrollIndex监听可视条目索引,判断是否滑到最后一条,触发加载下一页。
@Entry
@Component
struct ListLoadMoreDemo {
@State dataList: number[] = [];
@State page: number = 1;
@State isLoading: boolean = false;
aboutToAppear() {
this.loadData()
}
// 模拟接口请求分页数据
loadData() {
if(this.isLoading) return;
this.isLoading = true;
// 模拟网络延迟
setTimeout(()=>{
const temp: number[] = []
for(let i=0; i<10;i++){
temp.push((this.page-1)*10 + i +1)
}
this.dataList = [...this.dataList, ...temp]
this.page +=1
this.isLoading = false
}, 800)
}
build() {
Column() {
List() {
ForEach(this.dataList, (num:number)=>{
ListItem(){
Text(`列表条目 ${num}`)
.padding(16)
.fontSize(18)
}
}, (num:number)=>num.toString())
// 底部加载更多提示
ListItem(){
if(this.isLoading){
Text("正在加载更多...")
.width('100%')
.textAlign(TextAlign.Center)
.padding(16)
}else{
Text("上拉加载更多")
.width('100%')
.textAlign(TextAlign.Center)
.padding(16)
}
}
}
.width('100%')
.height('100%')
// 监听列表滚动索引
.onScrollIndex((start:number, end:number)=>{
// end是当前屏幕最后一条索引,等于数组最后下标,触发加载
if(end >= this.dataList.length - 1 && !this.isLoading){
this.loadData()
}
})
}
}
}
关键逻辑说明
isLoading锁:防止短时间多次触发加载,重复请求接口;onScrollIndex回调,拿到屏幕可见条目的首尾索引;- 滑到最后一条,调用加载函数追加数据;
- 追加数据时用
...展开生成新数组,触发 List 刷新。

四、下拉刷新 List 自带下拉刷新
鸿蒙 7 List 内置下拉刷新,不用自己手写动画。
@Entry
@Component
struct ListRefreshDemo {
@State dataList: number[] = [1,2,3,4,5];
@State isRefreshing: boolean = false;
// 下拉刷新回调
onRefresh() {
this.isRefreshing = true
setTimeout(()=>{
// 模拟重新请求第一页数据
this.dataList = [1,2,3,4,5]
this.isRefreshing = false
}, 1000)
}
build() {
Column() {
List() {
ForEach(this.dataList, (num:number)=>{
ListItem(){
Text(`条目 ${num}`).padding(16)
}
}, num=>num.toString())
}
.width('100%')
.height('100%')
.refresh({
refreshing: this.isRefreshing,
onRefresh: ()=> this.onRefresh()
})
}
}
}
refresh属性开启下拉刷新;refreshing绑定状态,true 展示 loading 转圈,加载完成置 false;- 下拉松手触发 onRefresh 回调,在这里重新拉取首页数据。

五、List 开发高频踩坑汇总
坑 1:List 没有设置 height,懒加载失效
List 必须有确定高度,100% 或者固定 px,否则 List 会一次性渲染全部条目,大数据直接卡顿。
坑 2:ForEach key 重复,列表 UI 错乱
第三个参数生成唯一 key,不能直接用数组索引 index 当 key。删除、新增数组元素时,index 复用会出现文字图片错位。 ❌错误写法:ForEach(arr, item=>{}, (item,index)=>index)
坑 3:ListItem 内部组件太复杂,Item 复用带来状态错乱
ListItem 内 @State 变量,滑动复用会保留旧状态。解决方案:列表数据全部放到外层数据源,ListItem 内部尽量不维护独立状态。
坑 4:上拉加载多次触发
没有 isLoading 加载锁,快速滑动到底部会连续多次请求接口,造成重复数据。加载中直接 return 拦截。
坑 5:长列表图片不做缓存,滑动卡顿
列表内 Image 组件建议设置.cached(true)开启图片缓存,减少重复解码开销。
六、List 功能选型速查表
表格
| 功能 | API | 适用场景 |
|---|---|---|
| 基础滚动列表 | List + ListItem + ForEach | 订单、消息、商品列表 |
| 分割线 | .divider() | 条目之间分隔 |
| 下拉刷新 | .refresh() | 下拉重新加载首页数据 |
| 上拉分页加载 | onScrollIndex 监听索引 | 滚动到底部加载下一页 |
结尾总结
List 是鸿蒙 7 长列表首选组件,核心优势是 Item 复用、懒加载,千万不要用 Column+ForEach 写上千条数据。
- List 必须设置高度,内部只能放 ListItem;
- ForEach 一定要提供稳定唯一 key,不要用数组下标;
- 分页记得加加载锁,避免重复请求;
- 内置 refresh 实现下拉刷新,onScrollIndex 监听滚动实现上拉加载;
- ListItem 内部尽量少维护本地状态,防止条目复用时 UI 错乱。
实操指南
结合本篇代码,实现一个同时支持下拉刷新 + 上拉加载更多的列表,每条 Item 展示序号,加载时显示 loading 提示。
更多推荐

所有评论(0)