乐趣区

关于harmonyos:HarmonyOSUI-开发性能提升的推荐方法

注:本文转载自 HarmonyOS 官网文档

开发者若应用低性能的代码实现性能场景可能不会影响利用的失常运行,但却会对利用的性能造成负面影响。本章节列举出了一些可晋升性能的场景供开发者参考,以防止利用实现上带来的性能劣化。

应用数据懒加载
开发者在应用长列表时,如果间接采纳循环渲染形式,如下所示,会一次性加载所有的列表元素,一方面会导致页面启动工夫过长,影响用户体验,另一方面也会减少服务器的压力和流量,减轻零碎负】

@Entry
@Component
struct MyComponent {@State arr: number[] = Array.from(Array(100), (v,k) =>k);  // 结构 0 -99 的数组
  build() {List() {ForEach(this.arr, (item: number) => {ListItem() {Text(`item value: ${item}`)
        }
      }, (item: number) => item.toString())
    }
  }
}

上述代码会在页面加载时将 100 个列表元素全副加载,这并非咱们须要的,咱们心愿从数据源中按需迭代加载数据并创立相应组件,因而须要应用数据懒加载,如下所示:

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []

  public totalCount(): number {return 0}

  public getData(index: number): any {return undefined}

  registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')
      this.listeners.push(listener)
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {console.info('remove listener')
      this.listeners.splice(pos, 1)
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {listener.onDataReloaded()
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {listener.onDataAdd(index)
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {listener.onDataChange(index)
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {listener.onDataDelete(index)
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {listener.onDataMove(from, to)
    })
  }
}

class MyDataSource extends BasicDataSource {private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']

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

  public getData(index: number): any {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)
  }
}

@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource()

  build() {List() {LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(20).margin({left: 10})
          }
        }
        .onClick(() => {this.data.pushData('item value:' + this.data.totalCount())
        })
      }, item => item)
    }
  }
}

上述代码在页面加载时仅初始化加载三个列表元素,之后每点击一次列表元素,将减少一个列表元素。

设置 List 组件的宽高
在应用 Scroll 容器组件嵌套 List 组件加载长列表时,若不指定 List 的宽高尺寸,则默认全副加载。

阐明
Scroll 嵌套 List 时:
● List 没有设置宽高,会布局 List 的所有子组件。
● List 设置宽高,会布局 List 显示区域内的子组件。
● List 应用 ForEach 加载子组件时,无论是否设置 List 的宽高,都会加载所有子组件。
● List 应用 LazyForEach 加载子组件时,没有设置 List 的宽高,会加载所有子组件,设置了 List 的宽高,会加载 List 显示区域内的子组件。

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []

  public totalCount(): number {return 0}

  public getData(index: number): any {return undefined}

  registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')
      this.listeners.push(listener)
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {console.info('remove listener')
      this.listeners.splice(pos, 1)
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {listener.onDataReloaded()
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {listener.onDataAdd(index)
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {listener.onDataChange(index)
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {listener.onDataDelete(index)
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {listener.onDataMove(from, to)
    })
  }
}

class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')

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

  public getData(index: number): any {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)
  }
}

@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource()

  build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Row() {Text('item value:' + item + (index + 1)).fontSize(20).margin(10)
            }
          }
        })
      }
    }
  }
}

因而,此场景下倡议设置 List 子组件的宽高。

class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = []

  public totalCount(): number {return 0}

  public getData(index: number): any {return undefined}

  registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('add listener')
      this.listeners.push(listener)
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {console.info('remove listener')
      this.listeners.splice(pos, 1)
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {listener.onDataReloaded()
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {listener.onDataAdd(index)
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {listener.onDataChange(index)
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {listener.onDataDelete(index)
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {listener.onDataMove(from, to)
    })
  }
}

class MyDataSource extends BasicDataSource {private dataArray: Array<string> = new Array(100).fill('test')

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

  public getData(index: number): any {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)
  }
}

@Entry
@Component
struct MyComponent {private data: MyDataSource = new MyDataSource()

  build() {Scroll() {List() {LazyForEach(this.data, (item: string, index: number) => {ListItem() {Text('item value:' + item + (index + 1)).fontSize(20).margin(10)
          }.width('100%')
        })
      }.width('100%').height(500)
    }.backgroundColor(Color.Pink)
  }
}

应用条件渲染代替显隐管制
如下所示,开发者在应用 visibility 通用属性管制组件的显隐状态时,仍存在组件的从新创立过程,造成性能上的损耗。

@Entry
@Component
struct MyComponent {
  @State isVisible: Visibility = Visibility.Visible;

  build() {Column() {Button("显隐切换")
        .onClick(() => {if (this.isVisible == Visibility.Visible) {this.isVisible = Visibility.None} else {this.isVisible = Visibility.Visible}
        })
      Row().visibility(this.isVisible)
        .width(300).height(300).backgroundColor(Color.Pink)
    }.width('100%')
  }
}

要防止这一问题,可应用 if 条件渲染代替 visibility 属性变换,如下所示:

@Entry
@Component
struct MyComponent {
  @State isVisible: boolean = true;

  build() {Column() {Button("显隐切换")
        .onClick(() => {this.isVisible = !this.isVisible})
      if (this.isVisible) {Row()
          .width(300).height(300).backgroundColor(Color.Pink)
      }
    }.width('100%')
  }
}

应用 Column/Row 代替 Flex
因为 Flex 容器组件默认状况下存在 shrink 导致二次布局,这会在肯定水平上造成页面渲染上的性能劣化。

@Entry
@Component
struct MyComponent {build() {Flex({ direction: FlexDirection.Column}) {Flex().width(300).height(200).backgroundColor(Color.Pink)
      Flex().width(300).height(200).backgroundColor(Color.Yellow)
      Flex().width(300).height(200).backgroundColor(Color.Grey)
    }
  }
}

上述代码可将 Flex 替换为 Column、Row,在保障实现的页面布局成果雷同的前提下防止 Flex 二次布局带来的负面影响。

@Entry
@Component
struct MyComponent {build() {Column() {Row().width(300).height(200).backgroundColor(Color.Pink)
      Row().width(300).height(200).backgroundColor(Color.Yellow)
      Row().width(300).height(200).backgroundColor(Color.Grey)
    }
  }
}

缩小利用滑动白块
利用通过增大 List/Grid 控件的 cachedCount 参数,调整 UI 的加载范畴。
cachedCount 示意屏幕外 List/Grid 预加载 item 的个数。
如果须要申请网络图片,能够在 item 滑动到屏幕显示之前,提前下载好内容,从而缩小滑动白块。
如下是应用 cachedCount 参数的例子:

@Entry
@Component
struct MyComponent {private source: MyDataSource = new MyDataSource();

  build() {List() {
      LazyForEach(this.source, item => {ListItem() {Text("Hello" + item)
            .fontSize(50)
            .onAppear(() => {console.log("appear:" + item)
            })
        }
      })
    }.cachedCount(3) // 扩充数值 appear 日志范畴会变大
  }
}

class MyDataSource implements IDataSource {data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

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

  public getData(index: number): any {return this.data[index]
  }

  registerDataChangeListener(listener: DataChangeListener): void { }

  unregisterDataChangeListener(listener: DataChangeListener): void {}}

应用阐明:
cachedCount 的减少会增大 UI 的 cpu、内存开销。应用时须要依据理论状况,综合性能和用户体验进行调整。

点击关注浏览原文,理解更多精彩资讯

退出移动版