本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

@Watch装饰器用于监听状态变量的变化,并在变化时执行指定的回调函数,非常适合处理一些副作用逻辑

一、@Watch 装饰器核心概述

事项 说明
装饰器参数 必填。需为一个字符串,指定回调函数名,例如 @Watch('onChanged') 。
可装饰的变量 所有被装饰器装饰的状态变量(如 @State@Prop@Link),不能监听常规变量 。
装饰器顺序建议 建议 @State@Prop@Link 等装饰器在 @Watch 装饰器之前 。
触发机制 通过严格相等===)判断变化,真正改变时才触发 。
初始化是否触发 。认为初始化不是状态改变,只有在后续状态改变时才会调用 。
回调函数参数 可选参数 changedPropertyName?: string,可用于标识当前变化的属性名 。

二、更新流程

  • 同步执行@Watch 的回调方法会在属性变更之后、组件重新渲染之前同步执行 。
  • 可能触发连锁更新:在 @Watch 的回调方法里如果改变了其他状态变量,这些变更也会立刻被观察到,并可能触发其他 @Watch 回调或组件渲染 。
  • 初始化不触发:在自定义组件的第一次初始化时,@Watch 装饰的方法不会被调用。只有在后续状态改变时,才会调用 @Watch 回调方法 。

三、注意事项

  1. 避免无限循环绝对不要在 @Watch 的回调方法里直接或间接修改它正在监听的那个状态变量。这会导致循环触发回调,造成无限循环 。

    错误示例@Watch('onCountUpdated') count: number = 0; onCountUpdated() { this.count++; } // 死循环!

  2. 关注性能@Watch 回调会在主线程同步执行,应仅执行快速运算避免在回调中进行耗时操作或异步操作(如 async await),这可能会阻塞UI渲染,导致性能问题 。
  3. 理解相等性判断@Watch 使用严格相等(===)判断变化。对于对象或数组,仅当引用地址改变时才会触发。如果你修改了对象内部的属性或数组的元素而未改变引用,@Watch 不会触发 。

四、使用示例

1. 监听数据变化并更新相关状态
@Entry
@Component
struct CountModifier {
  @State count: number = 0;

  build() {
    Column() {
      Button('add to basket')
        .onClick(() => {
          this.count++ // 点击按钮,count发生变化
        })
      TotalView({ count: this.count })
    }
  }
}

@Component
struct TotalView {
  @Prop @Watch('onCountUpdated') count: number = 0; // 监听count
  @State total: number = 0;

  // @Watch 回调
  onCountUpdated(): void {
    this.total += this.count; // count变化时,更新total
  }

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

点击按钮修改 countTotalView 组件中的 @Prop count 更新 → 触发 onCountUpdated 方法 → 更新 total → 文本重新渲染。

2. 与 @Link 组合使用,监听复杂数据

@Watch 也可以监听 @Link 装饰的复杂数据类型(如数组)的引用变化。

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

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

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

  build() {
    Column() {
      Button('Add Item')
        .onClick(() => {
          // 创建一个新数组并添加新项目,改变引用以触发@Watch
          this.shopBasket = this.shopBasket.concat(new PurchaseItem(20));
        })
      BasketViewer({ shopBasket: $shopBasket }) // 通过$符号传递Link
    }
  }
}

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

  // @Watch 回调
  onBasketUpdated(propName: string): void {
    this.totalPurchase = this.shopBasket.reduce((sum, i) => sum + i.price, 0);
    // 可以在这里添加更多逻辑,如折扣计算
  }

  build() {
    Column() {
      Text(`Total Price: $${this.totalPurchase.toFixed(2)}`)       ForEach(this.shopBasket,         (item: PurchaseItem) => {           Text(`Item ${item.id}: $${item.price}`)
        },
        (item: PurchaseItem) => item.id.toString()
      )
    }
  }
}

shopBasket 数组添加新项时,会触发 onBasketUpdated 回调,重新计算总价。

五、总结

@Watch 装饰器是 ArkTS 状态管理中一个功能强大且实用的工具。它通过监听状态变量的变化并执行相应的回调函数。

  • 用于在状态变量改变时执行特定逻辑 。
  • 通过严格相等===)判断值是否变化 。
  • 初始化时不触发回调 。
  • 切勿在回调函数中修改正在监听的状态变量自身,以免无限循环 。
  • 保持回调函数轻量高效,避免性能问题 。
  • @Watch内的参数必须是声明的方法名,否则编译报错。
  • 常规变量不能被@Watch装饰。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐