# 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 官网文档归纳总结而来的开发技巧供读者参考,也非常欢迎读者给本网站提供更多的开发样例。