arkts-rendering-control-best-practices.md 4.6 KB
Newer Older
Z
zhuzijia 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# 渲染控制优秀实践


为了帮助应用程序开发人员提高其应用程序质量,本章节面向开发者提供了多个在开发ArkUI应用中常见场景和易错问题,并给出了对应的解决方案。此外,还提供了同一场景下,推荐用法和不推荐用法的对比和解释说明,更直观地展示两者区别,从而帮助开发者学习如果正确地在应用开发中使用渲染控制,进行高性能开发。


## 在ForEach数据源中添加元素

在ForEach数据源中添加元素导致数组项ID重复。


### 不推荐用法

  下面示例使用ForEach方法迭代数组this.arr的每个元素,在Text组件进行显示,并在单击Text('Add arr element')时添加新的数组元素。
  
```ts
@Entry
@Component
struct Index {
  @State arr: number[] = [1,2,3];

  build() {
      Column() {
       ForEach(this.arr,
25
         (item: void) => {
Z
zhuzijia 已提交
26 27
           Text(`Item ${item}`)
         },
28
         (item: string) => item.toString())
Z
zhuzijia 已提交
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
       Text('Add arr element')
         .fontSize(20)
         .onClick(()=>{
           this.arr.push(4);  // arr新增的元素,其在ForEach内的键值均为'4'
           console.log("Arr elements: ", this.arr);
         })
      }
    }
}
```

点击两次Text('Add arr element')时,数组this.arr每次都会添加新元素 4。但是在ForEach循环渲染中,第三个参数(item)=> item.toString()需要生成Array每一个item对应的id值。该Array Id被要求是唯一的和稳定的。

- 唯一性:键值生成函数生成的每个数组项的id是不同的。

- 稳定性:当数组项ID发生变化时,ArkUI框架认为该数组项被替换或更改。

- ArkUI框架会对重复的ID告警,这种情况下框架的行为是未知的,特别是UI的更新在该场景下可能不起作用。

因此上述示例中,框架不会显示第二次及以后新添加的文本元素。因为这个元素不再是唯一的,他们都含有相同的id4。 但是如果删除ForEach第三个键值生成函数(item) => item.toString(),则触发onClick事件后每一个新添加的Text元素都会得到更新。这是因为框架使用了默认的Array id生成函数,即(item: any, index : number) => '${index}__${JSON.stringify(item)}'。它的兼容性更好但可能会导致不必要的UI更新,因此仍建议应用定义自己的键值生成函数。


## ForEach数据源更新

ForEach数据源更新时,数组项ID与原数组项ID重复不会重新创建该数组项。


### 不推荐用法

下面的示例定义了Index和Child两个组件。父组件Index有arr数组成员变量,初始值包含数字1、2、3。Child定义\@Prop value,接收父组件中arr数组中的一个元素。

  
```ts
@Component
struct Child {
64
  @Prop value: number = 0;
Z
zhuzijia 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
  build() {
    Text(`${this.value}`)
      .fontSize(50)
      .onClick(() => {
        this.value++  // 点击改变@Prop的值
      })
  }
}
@Entry
@Component
struct Index {
  @State arr: number[] = [1, 2, 3];
  build() {
    Row() {
      Column() {
        // 对照组
        Child({ value: this.arr[0] })
        Child({ value: this.arr[1] })
        Child({ value: this.arr[2] })
        Divider().height(5)
        ForEach(this.arr,
86
          (item: number) => {
Z
zhuzijia 已提交
87 88
            Child({ value: item })
          },
89
          (item: string) => item.toString()  // 键值,标识id
Z
zhuzijia 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
        )
        Text('Parent: replace entire arr')
          .fontSize(50)
          .onClick(() => {
            // 两个数组项内均含有'3',ForEach内的id没有发生变化
            // 意味着ForEach不会更新该Child实例,@Prop也不会在父组件处被更新
            this.arr = (this.arr[0] == 1) ? [3, 4, 5] : [1, 2, 3];
          })
      }
    }
  }
}
```

当触发文本组件Parent: replace entire arr的onClick事件时,状态变量数组arr根据自身第一个元素的值将被[3, 4, 5]或[1, 2, 3]替换,但是ForEach里初始被创建的\@Prop传入值为3的Child组件并不会更新。

因为,老数组和新数组初始均含有同一个值的元素(数字3),而且该元素生成的标识id在父组件里没有变化。因此ForEach没有识别出对应的Child实例需要被新的输入值更新,对应的子组件内\@Prop也没有更新。

![zh-cn_image_0000001604900446](figures/zh-cn_image_0000001604900446.png)

可以在arr中用一个唯一的元素代替重复的元素3来观察本事件的行为表现。当恰当的替换掉数组时,接下来应用的表现才是符合预期的。