# 18.1:性能优化
本节笔者根据 OpenHarmony 官网的指导文档简单总结了以下几种方式,可有效提升应用性能,避免应用实现上带来的性能劣化。
# 18.1.1:列表性能优化
在使用 List 或者 Grid 等滚动组件时,一般是借助 ForEach 或者 LazyForEach 实现,它们的主要区别是: ForEach 会一次性加载所有的列表元素,这种加载方式一方面会导致页面的启动时间过长影响用户体验,另一方面也会增加服务器的压力和流量,加重系统负担,而 LazyForEach 则是按需加载,它可以显著提升页面加载速度。
- 推荐使用数据懒加载 - @Entry @Component struct Index { @State dataSet: number[] = Array.from(Array(100), (v, k) => k); build() { List() { ForEach(this.dataSet, (item: number) => { ListItem() { Text(`item value: ${item}`) } }, (item: number) => item.toString()) } .width("100%") .height("100%") } }1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- 上述代码会在页面加载时将 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) } .width("100%") .height("100%") } }1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102- 上述代码在页面加载时仅初始化加载三个列表元素,之后每点击一次列表元素,将增加一个列表元素。 
- 合理设置应用滑动白块 - List和- Grid组件提供了 cachedCount 属性,该属性表示- List或者- Grid组件在视图外预加载- item的个数,应用可以通过设置改参数来调整 UI 的加载范围,比如需要请求网络图片,可以在- item滑动到屏幕显示之前提前加载好内容从而减少滑动白块。以下是使用 cachedCount 参数的例子:- @Entry @Component struct Index { private source: MyDataSource = new MyDataSource(); build() { List() { LazyForEach (this.source, item => { ListItem() { Text("Hello" + item) .fontSize(100) .onAppear(()=>{ console.log("appear:" + item) }) } }) }.cachedCount(3) // 扩大数值appear日志范围会变大 } } class MyDataSource implements IDataSource { private 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 { } }1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38- 📢:cachedCount 的增加会增大 UI 的 cpu 和内存开销。使用时需要根据实际情况,综合性能和用户体验进行调整。 
- 设置 - List组件的宽高- 开发者在使用 - Scroll组件嵌套- List组件时,若不指定- List组件的宽高,则- List默认加载全部数据,示例如下:- @Entry @Component struct Index { private dataSet: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] build() { Scroll() { List() { ForEach(this.dataSet, (item) => { ListItem() { Text(`item value: ${item}`).fontSize(30).margin({ left: 10 }) }.height(100) }, (item) => item.toString()) } }.backgroundColor(Color.Pink) } }1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- 当示例启动后, - List组件会加载所有的组件元素,在这种场景下建议开发者设置- List组件的宽高,如下所示:- @Entry @Component struct Index { private dataSet: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; build() { Scroll() { List() { ForEach(this.dataSet, (item) => { ListItem() { Text(`item value: ${item}`).fontSize(30).margin({ left: 10 }) } .height(100) }, (item) => item.toString()) } .width('100%') // 设置宽 .height(500) // 设置高 }.backgroundColor(Color.Pink) } }1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
# 18.1.2:使用 Column/Row 替代 Flex
由于 Flex 容器组件默认情况下存在 shrink 导致二次布局,这会在一定程度上造成页面渲染上的性能劣化,示例如下:
@Entry @Component struct Index {
  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)
    }
  }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
上述代码可将 Flex 替换为 Column、Row,在保证实现的页面布局效果相同的前提下避免 Flex 二次布局带来的负面影响。
@Entry @Component struct Index {
  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)
    }
  }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 18.1.3:使用条件渲染替代显隐控制
如下所示,开发者在使用 visibility 通用属性控制组件的显隐状态时,仍存在组件的重新创建过程,造成性能上的损耗。
@Entry @Component struct Index {
  @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%')
  }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
要避免这一问题,可使用 if 条件渲染代替 visibility 属性变换,如下所示:
@Entry @Component struct MyComponent {
  @State isVisible: boolean = true;
  build() {
    Column() {
      Button("显隐切换")
        .onClick(() => {
          this.isVisible = !this.isVisible
        })
      if(this.isVisible) { // 使用 if 语句
        Row()
          .width(300)
          .height(300)
          .backgroundColor(Color.Pink)
      }
    }.width('100%')
  }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 18.1.4:小结
以上就是笔者根据 OpenHarmony 官网文档归纳总结而来的开发技巧供读者参考,也非常欢迎读者给本网站提供更多的开发样例。
 
 