HarmonyOS中那些不为人知的坑点

HarmonyOS Next 性能优化方面

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

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

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

因此我们做个实验

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

ts 复制代码
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%')
  }
}

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

ts 复制代码
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%')
  }
}
结果可以发现在嵌套滑动组件的情况下给高度和不给高度真的会影响懒加载的使用!!(不给高度会一次性加载所有数据)

2.合理状态管理

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

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

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

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

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

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

3.合理使用布局容器

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

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

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

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

两个方向(抄官网的):

  • 移除冗余的节点

  • 使用扁平化布局减少节点数

1.移除冗余的节点

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

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

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

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

一马不平川!

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

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

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

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

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

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

相关推荐
__Benco3 小时前
OpenHarmony子系统开发 - 热管理(一)
人工智能·harmonyos
HMS Core4 小时前
鸿蒙版小红书如何让图库访问完全由“你”掌控
安全·华为·harmonyos
SuperHeroWu74 小时前
【HarmonyOS 5】鸿蒙中如何使用MQTT
mqtt·华为·harmonyos
环信即时通讯云6 小时前
环信鸿蒙版 UIKit 快速上手指南
华为·harmonyos
鸿蒙布道师9 小时前
鸿蒙NEXT开发资源工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
别说我什么都不会9 小时前
OpenHarmony 实战开发—— IPC与RPC通信
rpc·harmonyos
二十秒的勇气11 小时前
HarmonyOS:HMPermission权限请求框架
华为·harmonyos
HarmonyOS_SDK11 小时前
页面加载提速37.9%,“凯叔讲故事”做了什么
harmonyos
葱段12 小时前
【Harmony】ArkUI全局设置窗口背景颜色、页面背景颜色
harmonyos·arkts·arkui
鸿蒙布道师14 小时前
鸿蒙NEXT开发设备相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei