性能提升的推薦方法

2024-02-07 12:51 更新

開發(fā)者若使用低性能的代碼實現(xiàn)功能場景可能不會影響應(yīng)用的正常運(yùn)行,但卻會對應(yīng)用的性能造成負(fù)面影響。本章節(jié)列舉出了一些可提升性能的場景供開發(fā)者參考,以避免應(yīng)用實現(xiàn)上帶來的性能劣化。

使用數(shù)據(jù)懶加載

開發(fā)者在使用長列表時,如果直接采用循環(huán)渲染方式,如下所示,會一次性加載所有的列表元素,一方面會導(dǎo)致頁面啟動時間過長,影響用戶體驗,另一方面也會增加服務(wù)器的壓力和流量,加重系統(tǒng)負(fù)擔(dān)。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State arr: number[] = Array.from(Array(100), (v,k) =>k); //構(gòu)造0-99的數(shù)組
  5. build() {
  6. List() {
  7. ForEach(this.arr, (item: number) => {
  8. ListItem() {
  9. Text(`item value: ${item}`)
  10. }
  11. }, (item: number) => item.toString())
  12. }
  13. }
  14. }

上述代碼會在頁面加載時將100個列表元素全部加載,這并非我們需要的,我們希望從數(shù)據(jù)源中按需迭代加載數(shù)據(jù)并創(chuàng)建相應(yīng)組件,因此需要使用數(shù)據(jù)懶加載,如下所示:

  1. class BasicDataSource implements IDataSource {
  2. private listeners: DataChangeListener[] = []
  3. public totalCount(): number {
  4. return 0
  5. }
  6. public getData(index: number): any {
  7. return undefined
  8. }
  9. registerDataChangeListener(listener: DataChangeListener): void {
  10. if (this.listeners.indexOf(listener) < 0) {
  11. console.info('add listener')
  12. this.listeners.push(listener)
  13. }
  14. }
  15. unregisterDataChangeListener(listener: DataChangeListener): void {
  16. const pos = this.listeners.indexOf(listener);
  17. if (pos >= 0) {
  18. console.info('remove listener')
  19. this.listeners.splice(pos, 1)
  20. }
  21. }
  22. notifyDataReload(): void {
  23. this.listeners.forEach(listener => {
  24. listener.onDataReloaded()
  25. })
  26. }
  27. notifyDataAdd(index: number): void {
  28. this.listeners.forEach(listener => {
  29. listener.onDataAdd(index)
  30. })
  31. }
  32. notifyDataChange(index: number): void {
  33. this.listeners.forEach(listener => {
  34. listener.onDataChange(index)
  35. })
  36. }
  37. notifyDataDelete(index: number): void {
  38. this.listeners.forEach(listener => {
  39. listener.onDataDelete(index)
  40. })
  41. }
  42. notifyDataMove(from: number, to: number): void {
  43. this.listeners.forEach(listener => {
  44. listener.onDataMove(from, to)
  45. })
  46. }
  47. }
  48. class MyDataSource extends BasicDataSource {
  49. private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2']
  50. public totalCount(): number {
  51. return this.dataArray.length
  52. }
  53. public getData(index: number): any {
  54. return this.dataArray[index]
  55. }
  56. public addData(index: number, data: string): void {
  57. this.dataArray.splice(index, 0, data)
  58. this.notifyDataAdd(index)
  59. }
  60. public pushData(data: string): void {
  61. this.dataArray.push(data)
  62. this.notifyDataAdd(this.dataArray.length - 1)
  63. }
  64. }
  65. @Entry
  66. @Component
  67. struct MyComponent {
  68. private data: MyDataSource = new MyDataSource()
  69. build() {
  70. List() {
  71. LazyForEach(this.data, (item: string) => {
  72. ListItem() {
  73. Row() {
  74. Text(item).fontSize(20).margin({ left: 10 })
  75. }
  76. }
  77. .onClick(() => {
  78. this.data.pushData('item value: ' + this.data.totalCount())
  79. })
  80. }, item => item)
  81. }
  82. }
  83. }

上述代碼在頁面加載時僅初始化加載三個列表元素,之后每點擊一次列表元素,將增加一個列表元素。

設(shè)置List組件的寬高

在使用Scroll容器組件嵌套List組件加載長列表時,若不指定List的寬高尺寸,則默認(rèn)全部加載。

說明

Scroll嵌套List時:

  • List沒有設(shè)置寬高,會布局List的所有子組件。
  • List設(shè)置寬高,會布局List顯示區(qū)域內(nèi)的子組件。
  • List使用ForEach加載子組件時,無論是否設(shè)置List的寬高,都會加載所有子組件。
  • List使用LazyForEach加載子組件時,沒有設(shè)置List的寬高,會加載所有子組件,設(shè)置了List的寬高,會加載List顯示區(qū)域內(nèi)的子組件。
  1. class BasicDataSource implements IDataSource {
  2. private listeners: DataChangeListener[] = []
  3. public totalCount(): number {
  4. return 0
  5. }
  6. public getData(index: number): any {
  7. return undefined
  8. }
  9. registerDataChangeListener(listener: DataChangeListener): void {
  10. if (this.listeners.indexOf(listener) < 0) {
  11. console.info('add listener')
  12. this.listeners.push(listener)
  13. }
  14. }
  15. unregisterDataChangeListener(listener: DataChangeListener): void {
  16. const pos = this.listeners.indexOf(listener);
  17. if (pos >= 0) {
  18. console.info('remove listener')
  19. this.listeners.splice(pos, 1)
  20. }
  21. }
  22. notifyDataReload(): void {
  23. this.listeners.forEach(listener => {
  24. listener.onDataReloaded()
  25. })
  26. }
  27. notifyDataAdd(index: number): void {
  28. this.listeners.forEach(listener => {
  29. listener.onDataAdd(index)
  30. })
  31. }
  32. notifyDataChange(index: number): void {
  33. this.listeners.forEach(listener => {
  34. listener.onDataChange(index)
  35. })
  36. }
  37. notifyDataDelete(index: number): void {
  38. this.listeners.forEach(listener => {
  39. listener.onDataDelete(index)
  40. })
  41. }
  42. notifyDataMove(from: number, to: number): void {
  43. this.listeners.forEach(listener => {
  44. listener.onDataMove(from, to)
  45. })
  46. }
  47. }
  48. class MyDataSource extends BasicDataSource {
  49. private dataArray: Array<string> = new Array(100).fill('test')
  50. public totalCount(): number {
  51. return this.dataArray.length
  52. }
  53. public getData(index: number): any {
  54. return this.dataArray[index]
  55. }
  56. public addData(index: number, data: string): void {
  57. this.dataArray.splice(index, 0, data)
  58. this.notifyDataAdd(index)
  59. }
  60. public pushData(data: string): void {
  61. this.dataArray.push(data)
  62. this.notifyDataAdd(this.dataArray.length - 1)
  63. }
  64. }
  65. @Entry
  66. @Component
  67. struct MyComponent {
  68. private data: MyDataSource = new MyDataSource()
  69. build() {
  70. Scroll() {
  71. List() {
  72. LazyForEach(this.data, (item: string, index: number) => {
  73. ListItem() {
  74. Row() {
  75. Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)
  76. }
  77. }
  78. })
  79. }
  80. }
  81. }
  82. }

因此,此場景下建議設(shè)置List子組件的寬高。

  1. class BasicDataSource implements IDataSource {
  2. private listeners: DataChangeListener[] = []
  3. public totalCount(): number {
  4. return 0
  5. }
  6. public getData(index: number): any {
  7. return undefined
  8. }
  9. registerDataChangeListener(listener: DataChangeListener): void {
  10. if (this.listeners.indexOf(listener) < 0) {
  11. console.info('add listener')
  12. this.listeners.push(listener)
  13. }
  14. }
  15. unregisterDataChangeListener(listener: DataChangeListener): void {
  16. const pos = this.listeners.indexOf(listener);
  17. if (pos >= 0) {
  18. console.info('remove listener')
  19. this.listeners.splice(pos, 1)
  20. }
  21. }
  22. notifyDataReload(): void {
  23. this.listeners.forEach(listener => {
  24. listener.onDataReloaded()
  25. })
  26. }
  27. notifyDataAdd(index: number): void {
  28. this.listeners.forEach(listener => {
  29. listener.onDataAdd(index)
  30. })
  31. }
  32. notifyDataChange(index: number): void {
  33. this.listeners.forEach(listener => {
  34. listener.onDataChange(index)
  35. })
  36. }
  37. notifyDataDelete(index: number): void {
  38. this.listeners.forEach(listener => {
  39. listener.onDataDelete(index)
  40. })
  41. }
  42. notifyDataMove(from: number, to: number): void {
  43. this.listeners.forEach(listener => {
  44. listener.onDataMove(from, to)
  45. })
  46. }
  47. }
  48. class MyDataSource extends BasicDataSource {
  49. private dataArray: Array<string> = new Array(100).fill('test')
  50. public totalCount(): number {
  51. return this.dataArray.length
  52. }
  53. public getData(index: number): any {
  54. return this.dataArray[index]
  55. }
  56. public addData(index: number, data: string): void {
  57. this.dataArray.splice(index, 0, data)
  58. this.notifyDataAdd(index)
  59. }
  60. public pushData(data: string): void {
  61. this.dataArray.push(data)
  62. this.notifyDataAdd(this.dataArray.length - 1)
  63. }
  64. }
  65. @Entry
  66. @Component
  67. struct MyComponent {
  68. private data: MyDataSource = new MyDataSource()
  69. build() {
  70. Scroll() {
  71. List() {
  72. LazyForEach(this.data, (item: string, index: number) => {
  73. ListItem() {
  74. Text('item value: ' + item + (index + 1)).fontSize(20).margin(10)
  75. }.width('100%')
  76. })
  77. }.width('100%').height(500)
  78. }.backgroundColor(Color.Pink)
  79. }
  80. }

使用條件渲染替代顯隱控制

如下所示,開發(fā)者在使用visibility通用屬性控制組件的顯隱狀態(tài)時,仍存在組件的重新創(chuàng)建過程,造成性能上的損耗。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State isVisible: Visibility = Visibility.Visible;
  5. build() {
  6. Column() {
  7. Button("顯隱切換")
  8. .onClick(() => {
  9. if (this.isVisible == Visibility.Visible) {
  10. this.isVisible = Visibility.None
  11. } else {
  12. this.isVisible = Visibility.Visible
  13. }
  14. })
  15. Row().visibility(this.isVisible)
  16. .width(300).height(300).backgroundColor(Color.Pink)
  17. }.width('100%')
  18. }
  19. }

要避免這一問題,可使用if條件渲染代替visibility屬性變換,如下所示:

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. @State isVisible: boolean = true;
  5. build() {
  6. Column() {
  7. Button("顯隱切換")
  8. .onClick(() => {
  9. this.isVisible = !this.isVisible
  10. })
  11. if (this.isVisible) {
  12. Row()
  13. .width(300).height(300).backgroundColor(Color.Pink)
  14. }
  15. }.width('100%')
  16. }
  17. }

使用Column/Row替代Flex

由于Flex容器組件默認(rèn)情況下存在shrink導(dǎo)致二次布局,這會在一定程度上造成頁面渲染上的性能劣化。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. build() {
  5. Flex({ direction: FlexDirection.Column }) {
  6. Flex().width(300).height(200).backgroundColor(Color.Pink)
  7. Flex().width(300).height(200).backgroundColor(Color.Yellow)
  8. Flex().width(300).height(200).backgroundColor(Color.Grey)
  9. }
  10. }
  11. }

上述代碼可將Flex替換為Column、Row,在保證實現(xiàn)的頁面布局效果相同的前提下避免Flex二次布局帶來的負(fù)面影響。

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. build() {
  5. Column() {
  6. Row().width(300).height(200).backgroundColor(Color.Pink)
  7. Row().width(300).height(200).backgroundColor(Color.Yellow)
  8. Row().width(300).height(200).backgroundColor(Color.Grey)
  9. }
  10. }
  11. }

減少應(yīng)用滑動白塊

應(yīng)用通過增大List/Grid控件的cachedCount參數(shù),調(diào)整UI的加載范圍。cachedCount表示屏幕外List/Grid預(yù)加載item的個數(shù)。

如果需要請求網(wǎng)絡(luò)圖片,可以在item滑動到屏幕顯示之前,提前下載好內(nèi)容,從而減少滑動白塊。

如下是使用cachedCount參數(shù)的例子:

  1. @Entry
  2. @Component
  3. struct MyComponent {
  4. private source: MyDataSource = new MyDataSource();
  5. build() {
  6. List() {
  7. LazyForEach(this.source, item => {
  8. ListItem() {
  9. Text("Hello" + item)
  10. .fontSize(50)
  11. .onAppear(() => {
  12. console.log("appear:" + item)
  13. })
  14. }
  15. })
  16. }.cachedCount(3) // 擴(kuò)大數(shù)值appear日志范圍會變大
  17. }
  18. }
  19. class MyDataSource implements IDataSource {
  20. data: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
  21. public totalCount(): number {
  22. return this.data.length
  23. }
  24. public getData(index: number): any {
  25. return this.data[index]
  26. }
  27. registerDataChangeListener(listener: DataChangeListener): void {
  28. }
  29. unregisterDataChangeListener(listener: DataChangeListener): void {
  30. }
  31. }

使用說明:

cachedCount的增加會增大UI的cpu、內(nèi)存開銷。使用時需要根據(jù)實際情況,綜合性能和用戶體驗進(jìn)行調(diào)整。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號