针对鸿蒙(HarmonyOS)音乐项目中非首屏内容(更多歌单)的懒加载优化 ,核心思路是利用鸿蒙的LazyForEach组件(懒加载循环渲染组件),仅渲染可视区域内的歌单项,滚动到对应位置时再加载数据和渲染 DOM,大幅减少首屏渲染压力和内存占用。
完整代码案例(ArkTS + Stage 模型)
以下是基于鸿蒙最新的 ArkTS 语法实现的「更多歌单」懒加载示例,包含数据模拟、懒加载渲染、滚动加载更多等核心逻辑:
TypeScript
@Entry
@Component
struct MorePlaylistsPage {
// 歌单列表数据(模拟分页接口返回的结构)
@State playlists: Array<{ id: number, name: string, cover: string, author: string }> = [];
// 当前页码(用于分页加载)
@State currentPage: number = 1;
// 每页加载数量
private readonly PAGE_SIZE: number = 10;
// 是否正在加载(防止重复请求)
@State isLoading: boolean = false;
// LazyForEach需要的数据源(必须实现IDataSource接口)
private playlistDataSource: PlaylistDataSource = new PlaylistDataSource(this.playlists);
// 页面初始化时加载第一页数据
aboutToAppear() {
this.loadMorePlaylists();
}
// 加载更多歌单数据(模拟调用后端接口)
async loadMorePlaylists() {
if (this.isLoading) return; // 防止重复加载
this.isLoading = true;
try {
// 模拟接口请求(替换为真实的鸿蒙网络请求API)
const mockApiResponse = await new Promise<Array<{ id: number, name: string, cover: string, author: string }>>((resolve) => {
setTimeout(() => {
const newData = Array.from({ length: this.PAGE_SIZE }, (_, index) => ({
id: (this.currentPage - 1) * this.PAGE_SIZE + index + 1,
name: `热门歌单${(this.currentPage - 1) * this.PAGE_SIZE + index + 1}`,
cover: `https://example.com/cover${index + 1}.jpg`, // 替换为真实封面地址
author: `音乐人${index + 1}`
}));
resolve(newData);
}, 500); // 模拟接口延迟
});
// 将新数据追加到列表
this.playlists = [...this.playlists, ...mockApiResponse];
// 更新懒加载数据源
this.playlistDataSource.updateData(this.playlists);
// 页码+1
this.currentPage++;
} catch (e) {
console.error("加载歌单失败:", e);
} finally {
this.isLoading = false;
}
}
build() {
Column() {
// 页面标题
Text("更多歌单")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 16, bottom: 16 })
.alignSelf(ItemAlign.Center);
// 懒加载歌单列表(核心:LazyForEach)
List() {
LazyForEach(
this.playlistDataSource, // 懒加载数据源
(item: { id: number, name: string, cover: string, author: string }) => {
// 单个歌单项渲染(仅可视区域内的项会被渲染)
ListItem() {
PlaylistItem(playlist: item)
.margin({ bottom: 12 })
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
.shadow({ radius: 2, color: "#00000010", offsetX: 0, offsetY: 2 });
}
},
(item: { id: number }) => item.id.toString() // 唯一标识(必须)
)
}
.width('100%')
.height('100%')
.padding({ left: 16, right: 16 })
// 滚动到底部时加载更多(触底加载)
.onReachEnd(() => {
this.loadMorePlaylists();
})
// 加载中占位
.placeholder({
fallback: () => {
Text("正在加载歌单...")
.fontSize(14)
.color(Color.Grey)
.margin({ top: 20 });
}
});
// 加载中提示
if (this.isLoading) {
LoadingProgress()
.width(20)
.height(20)
.margin({ top: 10 });
}
}
.backgroundColor("#F5F5F5")
.width('100%')
.height('100%');
}
}
// 单个歌单项组件
@Component
struct PlaylistItem {
private playlist: { id: number, name: string, cover: string, author: string };
build() {
Row() {
// 歌单封面(懒加载渲染,仅显示时加载)
Image(this.playlist.cover)
.width(60)
.height(60)
.borderRadius(4)
.objectFit(ImageFit.Cover);
// 歌单信息
Column() {
Text(this.playlist.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.maxLines(1)
.width('100%');
Text(`by ${this.playlist.author}`)
.fontSize(12)
.color(Color.Grey)
.margin({ top: 4 })
.maxLines(1)
.width('100%');
}
.margin({ left: 12 })
.flexGrow(1);
}
.width('100%');
}
}
// 实现LazyForEach所需的数据源接口
class PlaylistDataSource implements IDataSource {
private dataArray: Array<{ id: number, name: string, cover: string, author: string }> = [];
private listener: DataChangeListener | undefined;
constructor(data: Array<{ id: number, name: string, cover: string, author: string }>) {
this.dataArray = data;
}
// 获取数据总数
totalCount(): number {
return this.dataArray.length;
}
// 获取指定索引的数据
getData(index: number): { id: number, name: string, cover: string, author: string } {
return this.dataArray[index];
}
// 注册数据变更监听器
registerDataChangeListener(listener: DataChangeListener): void {
this.listener = listener;
}
// 取消注册监听器
unregisterDataChangeListener(): void {
this.listener = undefined;
}
// 更新数据源并通知刷新
updateData(newData: Array<{ id: number, name: string, cover: string, author: string }>): void {
this.dataArray = newData;
this.listener?.onDataReloaded(); // 通知数据重新加载
}
}
代码核心说明
-
LazyForEach 核心懒加载:
- 鸿蒙的
LazyForEach是专门用于长列表懒加载的组件,区别于普通ForEach(一次性渲染所有项),它仅渲染可视区域内的歌单项,滚动到对应位置时才加载后续项,大幅降低首屏渲染耗时和内存占用。 - 必须实现
IDataSource接口作为数据源,确保懒加载逻辑的标准化。
- 鸿蒙的
-
触底加载更多:
- 通过
List的onReachEnd事件实现滚动到底部自动加载下一页歌单,结合isLoading状态防止重复请求,避免接口压力。
- 通过
-
资源懒加载:
- 歌单封面图片
Image组件在鸿蒙中默认具备懒加载特性(仅当组件进入可视区域时才会发起图片请求),配合LazyForEach进一步减少非首屏资源的加载。
- 歌单封面图片
-
性能优化细节:
- 给
List设置placeholder占位符,提升加载体验; - 歌单项文字设置
maxLines: 1避免文本换行导致的布局重绘; - 使用
flexGrow: 1和百分比布局,适配不同屏幕尺寸,减少布局计算开销。
- 给
总结
- 鸿蒙音乐项目中核心用
LazyForEach替代普通ForEach实现非首屏歌单的懒加载,仅渲染可视区域内容; - 结合
List的onReachEnd实现触底分页加载,配合加载状态控制避免重复请求; - 图片和文本组件的轻量化配置(如
maxLines、objectFit)进一步优化渲染性能。