鸿蒙 状态变量更改通知@Watch解析
本文介绍了ArkTS中的@Watch装饰器,它用于监听状态变量变化并执行回调函数。@Watch只能装饰@State、@Prop、@Link等状态变量,通过严格相等(===)判断变化,初始化不触发。回调函数同步执行,应避免无限循环和耗时操作。文章通过计数器修改和购物车示例展示了@Watch的典型用法,强调其适合处理状态变化的副作用逻辑,但需注意性能和循环引用问题。@Watch是ArkTS状态管理的重
·
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
@Watch装饰器用于监听状态变量的变化,并在变化时执行指定的回调函数,非常适合处理一些副作用逻辑。
一、@Watch 装饰器核心概述
事项 | 说明 |
---|---|
装饰器参数 | 必填。需为一个字符串,指定回调函数名,例如 @Watch('onChanged') 。 |
可装饰的变量 | 所有被装饰器装饰的状态变量(如 @State , @Prop , @Link ),不能监听常规变量 。 |
装饰器顺序建议 | 建议 @State 、@Prop 、@Link 等装饰器在 @Watch 装饰器之前 。 |
触发机制 | 通过严格相等(=== )判断变化,值真正改变时才触发 。 |
初始化是否触发 | 否。认为初始化不是状态改变,只有在后续状态改变时才会调用 。 |
回调函数参数 | 可选参数 changedPropertyName?: string ,可用于标识当前变化的属性名 。 |
二、更新流程
- 同步执行:
@Watch
的回调方法会在属性变更之后、组件重新渲染之前同步执行 。 - 可能触发连锁更新:在
@Watch
的回调方法里如果改变了其他状态变量,这些变更也会立刻被观察到,并可能触发其他@Watch
回调或组件渲染 。 - 初始化不触发:在自定义组件的第一次初始化时,
@Watch
装饰的方法不会被调用。只有在后续状态改变时,才会调用@Watch
回调方法 。
三、注意事项
- 避免无限循环:绝对不要在
@Watch
的回调方法里直接或间接修改它正在监听的那个状态变量。这会导致循环触发回调,造成无限循环 。错误示例:
@Watch('onCountUpdated') count: number = 0; onCountUpdated() { this.count++; } // 死循环!
- 关注性能:
@Watch
回调会在主线程同步执行,应仅执行快速运算。避免在回调中进行耗时操作或异步操作(如async await
),这可能会阻塞UI渲染,导致性能问题 。 - 理解相等性判断:
@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}`)
}
}
点击按钮修改 count
→ TotalView
组件中的 @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装饰。
更多推荐
所有评论(0)