HarmonyOS Next 性能优化方面

举报
yd_273695820 发表于 2025/06/14 16:07:57 2025/06/14
【摘要】 HarmonyOS Next 性能优化方面这篇文章的主要目的是介绍在鸿蒙中比较容易被忽视的坑点,如果你不去仔细查看华为的官方文档就会踩坑!! 1.大量数据并且使用懒加载情况下的Scroll滑动组件嵌套List滑动组件(滑动组件:Scroll,List等可以滑动的组件)子滑动组件一定要指定宽高!!文档链接在许多场景下都会用到长列表配上懒加载,并且在一些特定的UI需求下需要嵌套list等滑动组...

HarmonyOS Next 性能优化方面

这篇文章的主要目的是介绍在鸿蒙中比较容易被忽视的坑点,如果你不去仔细查看华为的官方文档就会踩坑!!

1.大量数据并且使用懒加载情况下的Scroll滑动组件嵌套List滑动组件(滑动组件:Scroll,List等可以滑动的组件)子滑动组件一定要指定宽高!!文档链接

在许多场景下都会用到长列表配上懒加载,并且在一些特定的UI需求下需要嵌套list等滑动组件,大部分情况下大家都不喜欢给子滑动组件高度(我也不喜欢)。这样就会导致一次性把数据全部加载,并且数据量特别大时滑动列表时掉帧,打开关闭页面掉帧,从而陷入自我怀疑!(这懒加载为什么用了还卡!!)

因此我们做个实验

1.嵌套滑动组件情况下不给list高度

export class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}
export class MyDataSource extends BasicDataSource {
  private dataArray: string[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public pushList(data: string[]): void {
    data.forEach(element => {
      this.dataArray.push(element);
    });
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct Index {
  private data: MyDataSource = new MyDataSource();
  private scroller: Scroller = new Scroller();

  aboutToAppear() {
    for (let i = 0; i <= 30; i++) {
      this.data.pushData(`Hello ${i}`)
    }
  }

  build() {
    Column() {
      Scroll(this.scroller) {
        Column() {
          List() {
            LazyForEach(this.data, (lazyForEachItem: string, index) => {
              ListItem() {
                Text(lazyForEachItem)
                  .fontSize(50)
                  .width('100%')
                  .onAppear(() => {
                    AlertDialog.show({ message: JSON.stringify(index, null, 2) })
                  })
              }

            })
          }
          .width('100%')
          // .layoutWeight(1)
        }
      }
      .width('100%')
      .height('100%')
    }
    .width('100%')
  }
}

HarmonyOS中那些不为人知的坑点1.pic.jpg

2.嵌套滑动组件情况下给list高度

export class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}
export class MyDataSource extends BasicDataSource {
  private dataArray: string[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }

  public pushList(data: string[]): void {
    data.forEach(element => {
      this.dataArray.push(element);
    });
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct Index {
  private data: MyDataSource = new MyDataSource();
  private scroller: Scroller = new Scroller();

  aboutToAppear() {
    for (let i = 0; i <= 30; i++) {
      this.data.pushData(`Hello ${i}`)
    }
  }

  build() {
    Column() {
      Scroll(this.scroller) {
        Column() {
          List() {
            LazyForEach(this.data, (lazyForEachItem: string, index) => {
              ListItem() {
                Text(lazyForEachItem)
                  .fontSize(50)
                  .width('100%')
                  .onAppear(() => {
                    AlertDialog.show({ message: JSON.stringify(index, null, 2) })
                  })
              }

            })
          }
          .width('100%')
          .layoutWeight(1)
        }
      }
      .width('100%')
      .height('100%')
    }
    .width('100%')
  }
}

HarmonyOS中那些不为人知的坑点2.pic.jpg

结果可以发现在嵌套滑动组件的情况下给高度和不给高度真的会影响懒加载的使用!!(不给高度会一次性加载所有数据)

2.合理状态管理

真实案例:我一个好朋友的同事把使用颜色的色值全部使用了@state。。。

HarmonyOS中那些不为人知的坑点3.pic.jpg

实际上这些使用了@state的数据不会再改变,看到这样的状态管理相信大家和我一样血压开始飙升了。。。

所以咱们该咋玩这个@State呢?

1.首先第一点肯定是别乱用啦,不需要去改变视图的数据就不需要使用@state,简单来说就是你这个数据改变跟你的UI变化有没有关系,从这个方向再去考虑要不要使用@State。

2.减少不必要的参数层层传递(疯狂的向子组件传入@prop,@link等)

官网的总结肯定比我好,大家直接通过链接学习一下,我这边就直接copy一下官网的总结啦

image-20241212142425382.png

3.合理使用布局容器

相信大家在画UI的时候为了快速完成任务而大量嵌套布局(怎么舒服怎么来),这样就会导致在数据量和状态量大的时候会卡顿。

1.对于布局最重要其实就是减少嵌套

那为什么要减少嵌套呢,其实和数据结构中树的概念是一样的,每一个组件就是一颗树的子节点,你的树越高(节点嵌套越深),时间复杂度就会高(读取更加深层的组件就越费时间)

2.那到底如何才能减少嵌套呢

两个方向(抄官网的):

  • 移除冗余的节点
  • 使用扁平化布局减少节点数
1.移除冗余的节点

简单一句话:减少没有必要的嵌套

image-20241212143326403.png

像这种row就是没用的嵌套就可以去除,在实际开发中尽量不影响UI结构的情况下把垃圾容器嵌套统统去除。

2.使用扁平化布局减少节点数。

啥叫扁平化?说白了(我白说了)就是让你的结构不深,看起来一马平川!

image-20241212143549713.png

一马不平川!

image-20241212143627669.png

那遇到复杂的场景我用线性布局肯定很难做到一马平川啊,那就可以使用相对布局RelativeContainer绝对定位 通过锚点定位实现扁平化,Grid 通过二维布局实现扁平化(我很少用)

相对布局是现在华为比较主推的一种布局方式了,但是对于一直用线性布局的老铁来说有点难以接受。

3.最后就是锦上添花的一些小操作了

1.大多数情况下用ifelse条件渲染代替visibility显隐控制

2.那什么时候不是大多数情况呢(直接甩出官网测试的结论)

image-20241212144316643.png

3.还有一点:对于一些可以给宽高的场景尽量给出宽高,因为编译的时候不给宽高,就会让它自己去计算宽高,从而导致了时间浪费,并且在一些场景中不给图片(能给占位图就给占位图)以及组件宽高(让它自适应),会出现加载数据的时候布局会互相挤压(体验糟糕)

##鸿蒙特性 ##鸿蒙性能

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。