arkts-watch.md 5.7 KB
Newer Older
H
HelloCrease 已提交
1
# \@Watch装饰器:状态变量更改通知
Z
zengyawen 已提交
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


\@Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用\@Watch为状态变量设置回调函数。


> **说明:**
>
> 从API version 9开始,该装饰器支持在ArkTS卡片中使用。


## 概述

\@Watch用于监听状态变量的变化,当状态变量变化时,\@Watch的回调方法将被调用。\@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发\@Watch的回调。


## 装饰器说明

| \@Watch补充变量装饰器 | 说明                                       |
| -------------- | ---------------------------------------- |
| 装饰器参数          | 必填。常量字符串,字符串需要有引号。是(string) => void自定义成员函数的方法的引用。 |
| 可装饰的自定义组件变量    | 可监听所有装饰器装饰的状态变量。不允许监听常规变量。               |
| 装饰器的顺序         | 建议\@State、\@Prop、\@Link等装饰器在\@Watch装饰器之前。 |


## 语法说明

| 类型                                       | 说明                                       |
| ---------------------------------------- | ---------------------------------------- |
| (changedPropertyName?&nbsp;:&nbsp;string)&nbsp;=&gt;&nbsp;void | 该函数是自定义组件的成员函数,changedPropertyName是被watch的属性名。<br/>在多个状态变量绑定同一个\@Watch的回调方法的时候,可以通过changedPropertyName进行不同的逻辑处理<br/>将属性名作为字符串输入参数,不返回任何内容。 |


## 观察变化和行为表现

1. 当观察到状态变量的变化(包括双向绑定的AppStorage和LocalStorage中对应的key发生的变化)的时候,对应的\@Watch的回调方法将被触发;

2. \@Watch方法在自定义组件的属性变更之后同步执行;

3. 如果在\@Watch的方法里改变了其他的状态变量,也会引起的状态变更和\@Watch的执行;

4. 在第一次初始化的时候,\@Watch装饰的方法不会被调用,即认为初始化不是状态变量的改变。只有在后续状态改变时,才会调用\@Watch回调方法。


## 限制条件

- 建议开发者避免无限循环。循环可能是因为在\@Watch的回调方法里直接或者间接地修改了同一个状态变量引起的。为了避免循环的产生,建议不要在\@Watch的回调方法里修改当前装饰的状态变量;

- 开发者应关注性能,属性值更新函数会延迟组件的重新渲染(具体请见上面的行为表现),因此,回调函数应仅执行快速运算;

- 不建议在\@Watch函数中调用async await,因为\@Watch设计的用途是为了快速的计算,异步行为可能会导致重新渲染速度的性能问题。


## 使用场景

55 56 57 58 59 60 61 62
### \@Watch和自定义组件更新

以下示例展示组件更新和\@Watch的处理步骤。count在CountModifier中由\@State装饰,在TotalView中由\@Prop装饰。


```ts
@Component
struct TotalView {
J
junyi233 已提交
63
  @Prop @Watch('onCountUpdated') count: number = 0;
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
  @State total: number = 0;
  // @Watch cb
  onCountUpdated(propName: string): void {
    this.total += this.count;
  }

  build() {
    Text(`Total: ${this.total}`)
  }
}

@Entry
@Component
struct CountModifier {
  @State count: number = 0;

  build() {
    Column() {
      Button('add to basket')
        .onClick(() => {
          this.count++
        })
      TotalView({ count: this.count })
    }
  }
}
```

处理步骤:

1. CountModifier自定义组件的Button.onClick点击事件自增count。

2. 由于\@State count变量更改,子组件TotalView中的\@Prop被更新,其\@Watch('onCountUpdated')方法被调用,更新了子组件TotalView 中的total变量。

3. 子组件TotalView中的Text重新渲染。

Z
zengyawen 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

### \@Watch与\@Link组合使用

以下示例说明了如何在子组件中观察\@Link变量。


```ts
class PurchaseItem {
  static NextId: number = 0;
  public id: number;
  public price: number;

  constructor(price: number) {
    this.id = PurchaseItem.NextId++;
    this.price = price;
  }
}

@Component
struct BasketViewer {
  @Link @Watch('onBasketUpdated') shopBasket: PurchaseItem[];
  @State totalPurchase: number = 0;

  updateTotal(): number {
    let total = this.shopBasket.reduce((sum, i) => sum + i.price, 0);
    // 超过100欧元可享受折扣
    if (total >= 100) {
      total = 0.9 * total;
    }
    return total;
  }
  // @Watch 回调
  onBasketUpdated(propName: string): void {
    this.totalPurchase = this.updateTotal();
  }

  build() {
    Column() {
      ForEach(this.shopBasket,
J
junyi233 已提交
139
        (item:PurchaseItem) => {
Z
zengyawen 已提交
140 141
          Text(`Price: ${item.price.toFixed(2)} €`)
        },
J
junyi233 已提交
142
        (item:PurchaseItem) => item.id.toString()
Z
zengyawen 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
      )
      Text(`Total: ${this.totalPurchase.toFixed(2)} €`)
    }
  }
}

@Entry
@Component
struct BasketModifier {
  @State shopBasket: PurchaseItem[] = [];

  build() {
    Column() {
      Button('Add to basket')
        .onClick(() => {
          this.shopBasket.push(new PurchaseItem(Math.round(100 * Math.random())))
        })
      BasketViewer({ shopBasket: $shopBasket })
    }
  }
}
```

处理步骤如下:

1. BasketModifier组件的Button.onClick向BasketModifier shopBasket中添加条目;

2. \@Link装饰的BasketViewer shopBasket值发生变化;

172
3. 状态管理框架调用\@Watch函数BasketViewer onBasketUpdated 更新BasketViewer TotalPurchase的值;
Z
zengyawen 已提交
173 174

4. \@Link shopBasket的改变,新增了数组项,ForEach组件会执行item Builder,渲染构建新的Item项;\@State totalPurchase改变,对应的Text组件也重新渲染;重新渲染是异步发生的。