鸿蒙音乐项目懒加载优化实战

针对鸿蒙(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(); // 通知数据重新加载
  }
}

代码核心说明

  1. LazyForEach 核心懒加载

    • 鸿蒙的LazyForEach是专门用于长列表懒加载的组件,区别于普通ForEach(一次性渲染所有项),它仅渲染可视区域内的歌单项,滚动到对应位置时才加载后续项,大幅降低首屏渲染耗时和内存占用。
    • 必须实现IDataSource接口作为数据源,确保懒加载逻辑的标准化。
  2. 触底加载更多

    • 通过ListonReachEnd事件实现滚动到底部自动加载下一页歌单,结合isLoading状态防止重复请求,避免接口压力。
  3. 资源懒加载

    • 歌单封面图片Image组件在鸿蒙中默认具备懒加载特性(仅当组件进入可视区域时才会发起图片请求),配合LazyForEach进一步减少非首屏资源的加载。
  4. 性能优化细节

    • List设置placeholder占位符,提升加载体验;
    • 歌单项文字设置maxLines: 1避免文本换行导致的布局重绘;
    • 使用flexGrow: 1和百分比布局,适配不同屏幕尺寸,减少布局计算开销。

总结

  1. 鸿蒙音乐项目中核心用LazyForEach替代普通ForEach实现非首屏歌单的懒加载,仅渲染可视区域内容;
  2. 结合ListonReachEnd实现触底分页加载,配合加载状态控制避免重复请求;
  3. 图片和文本组件的轻量化配置(如maxLinesobjectFit)进一步优化渲染性能。
相关推荐
吴声子夜歌2 小时前
JavaScript——对象
开发语言·javascript·ecmascript
不会写DN3 小时前
Js常用的字符串处理
开发语言·前端·javascript
晓13133 小时前
第三章 TypeScript 高级类型
前端·javascript·typescript
SuperEugene3 小时前
Vue keep-alive 实战避坑:include/exclude + 路由 meta 标记,中后台路由缓存精准可控|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·缓存·前端框架
被考核重击3 小时前
虚拟列表(动态高度,性能优化,骨架屏)
javascript·vue.js·性能优化·虚拟列表
用户9714171814273 小时前
JavaScript 模块化详解:CommonJS、ES Module 到底有什么区别?
javascript
薛定猫AI3 小时前
【技术干货】用 Stitch + AI 编码代理自动化前端开发:从设计到 Next.js 生产应用
javascript·人工智能·自动化
用户9714171814274 小时前
原型与原型链
javascript
cmd4 小时前
吃透 ES6 Generator:yield/next/yield* 核心用法详解
前端·javascript