一、@Watch 裝飾器:狀態(tài)變化的實(shí)時(shí)監(jiān)聽(tīng)者
核心功能
- @Watch用于監(jiān)聽(tīng)可觀察狀態(tài)變量(如@State/@Prop/@Link)的變化,在變量值發(fā)生變動(dòng)時(shí)觸發(fā)回調(diào)函數(shù)。其本質(zhì)是建立觀察者模式,實(shí)現(xiàn)組件間狀態(tài)同步
- @Watch適用于需要即時(shí)響應(yīng)的狀態(tài)變化場(chǎng)景,如按鈕點(diǎn)擊、輸入框輸入等交互操作
關(guān)鍵特性
- 監(jiān)聽(tīng)原理:通過(guò)嚴(yán)格相等(
===)算法判斷狀態(tài)變量是否更新(false時(shí)更新)
- 初始化階段:首次組件渲染時(shí)不會(huì)觸發(fā)回調(diào),僅后續(xù)狀態(tài)變更時(shí)激活,避免初始渲染時(shí)的誤判
- 異步安全:禁止在回調(diào)中使用
async/await,確保渲染線程的及時(shí)響應(yīng)
- 同步執(zhí)行:在屬性變更后立即觸發(fā),優(yōu)先級(jí)高于 UI 渲染
- 鏈?zhǔn)礁拢喝艋卣{(diào)中修改其他狀態(tài)變量,會(huì)引發(fā)二次監(jiān)聽(tīng)
- 節(jié)流防抖:高頻操作場(chǎng)景添加100ms延遲執(zhí)行
- 回調(diào)函數(shù):接收一個(gè)參數(shù)
propName,表示觸發(fā)變化的屬性名
- 條件過(guò)濾:在回調(diào)入口添加變更有效性判斷
@Watch('onDataUpdate')
private onDataUpdate(propName: string) {
if (this.prevValue !== this.currentValue) {
// 實(shí)際業(yè)務(wù)邏輯
}
}
- 異步操作規(guī)范: 避免在回調(diào)中使用async/await,推薦將異步操作封裝至獨(dú)立Service層,通過(guò)事件總線與狀態(tài)管理解耦
- 觸發(fā)條件:
- 值類型變化(如
string → number)
- 數(shù)值變化(如
0 → 1)
- 對(duì)象引用變化(如
new Object())
典型使用場(chǎng)景
- 電商購(gòu)物車實(shí)時(shí)計(jì)價(jià): 價(jià)格計(jì)算邏輯與UI更新解耦
@Observed //通過(guò)@Observed實(shí)現(xiàn)嵌套對(duì)象深度監(jiān)聽(tīng)
class CartItem {
constructor(public id: number, public price: number) {}
}
@Component
struct CheckoutPanel {
//@Watch與@Link配合實(shí)現(xiàn)雙向數(shù)據(jù)流
@Link @Watch('updateTotal') cartItems: CartItem[]
@State total: number = 0
updateTotal() {
this.total = this.cartItems.reduce((sum, item) => sum + item.price, 0)
if (this.total > 1000) this.total *= 0.8 // 滿減邏輯
}
build() {
Column() {
ForEach(this.cartItems, item =>
Text(`商品${item.id}:¥${item.price}`)
)
Text(`實(shí)付:¥${this.total.toFixed(2)}`)
}
}
}
- 跨組件狀態(tài)同步: 支持多頁(yè)面實(shí)時(shí)響應(yīng)主題變化
@Entry
@Component
struct AppRoot {
@Provide('theme') @Watch('themeChange') currentTheme: Theme = lightTheme
themeChange() {
Logger.log(`主題切換至${this.currentTheme.name}`)
}
}
@Component
struct SettingsPage {
@Consume('theme') theme: Theme
build() {
Toggle({ checked: this.theme.darkMode })
.onChange(value => this.theme.darkMode = value)
}
}
- 防循環(huán)陷阱:避免在回調(diào)中直接修改被監(jiān)聽(tīng)的同一狀態(tài)變量
@Component struct SafeCounter {
@State count: number = 0;
@Watch('onChange')
onChange() {
setTimeout(() => this.count++, 0); // 使用中間層避免直接修改
}
}
二、$$語(yǔ)法:動(dòng)態(tài)模板字符串插值
基礎(chǔ)語(yǔ)法
- 在 ArkTS 中,使用雙美元符號(hào)
$$ 實(shí)現(xiàn)字符串模板插值,將變量動(dòng)態(tài)嵌入字符串中
let name = "Alice";
let greeting = $$`Hello, ${name}!`;
// 輸出: Hello, Alice!
// 簡(jiǎn)單插值
Text(`Hello, ${this.username}!`);
// 表達(dá)式支持
Text(`Price: $${(this.price * 0.9).toFixed(2)}`);
// 動(dòng)態(tài)樣式綁定
Row() {
Text(`Status: ${
this.status === 'success' ? '?' : '?'
}`)
.color(this.getStatusColor())
}
- 避免重復(fù)計(jì)算:將復(fù)雜表達(dá)式提取為方法
getFormattedPrice() {
return this.price.toFixed(2);
}
Text(`Price: $${this.getFormattedPrice()}`)
三、@Track 裝飾器:對(duì)象屬性級(jí)更新優(yōu)化
核心功能
- 針對(duì)class對(duì)象的屬性級(jí)更新優(yōu)化,解決傳統(tǒng)狀態(tài)管理中全量刷新的性能問(wèn)題。通過(guò)標(biāo)記特定屬性,實(shí)現(xiàn)增量渲染。
- @Track推薦用于管理包含多個(gè)屬性的復(fù)雜對(duì)象,特別是在頻繁更新且需要優(yōu)化渲染性能的場(chǎng)景
運(yùn)行機(jī)制
- 白名單機(jī)制:僅跟蹤被
@Track標(biāo)記的屬性,避免未跟蹤屬性的全量刷新
- 必要標(biāo)記原則:所有可能在UI中使用的class屬性都應(yīng)添加
@Track
- 屬性級(jí)監(jiān)聽(tīng):基于JavaScript的
Proxy實(shí)現(xiàn)屬性訪問(wèn)攔截
- 增量渲染:僅更新被標(biāo)記屬性關(guān)聯(lián)的 UI 節(jié)點(diǎn)
- 兼容性:與 @State、@Link 等裝飾器無(wú)縫集成
- 避免混合模式:同一class對(duì)象中不應(yīng)同時(shí)存在
@Track與非@Track屬性
- 性能考量:對(duì)于包含大量屬性的復(fù)雜對(duì)象,優(yōu)先使用結(jié)構(gòu)化數(shù)據(jù)(如Record)
典型用例
// 跟蹤類定義
class Product {
@Track price: number = 100;
@Track stock: number = 50;
description: string = "Default Product";
}
// 組件使用
@Component struct ProductCard {
@State product: Product = new Product();
build() {
Column([
Text(`Price: $${this.product.price}`).fontSize(20),
Text(`Stock: ${this.product.stock}`).fontSize(20),
Button('Update Price').onClick(() => this.product.price += 10)
])
}
}
四、裝飾器協(xié)同模式
@Watch + @Track 組合
class DataModel {
@Track name: string;
@Track age: number;
}
@Component struct DataView {
@State model: DataModel = new DataModel();
@Watch('name') onChangeName() {
console.log(`Name changed to ${this.model.name}`);
}
@Watch('age') onChangeAge() {
console.log(`Age changed to ${this.model.age}`);
}
}
$$語(yǔ)法與狀態(tài)管理結(jié)合
@Component struct UserProfile {
@State user: { name: string; avatar: string } = { name: 'Alice', avatar: 'default' };
build() {
Column([
Text(`Welcome, ${this.user.name}!`),
Image(this.user.avatar)
.width(100)
.height(100)
])
}
}
五、總結(jié)與建議
六、常見(jiàn)問(wèn)題
- 未觸發(fā)回調(diào):
- 檢查
@Watch 參數(shù)是否與屬性名完全匹配
- 確認(rèn)狀態(tài)變量是
@State/@Prop/@Link 裝飾的
- 渲染異常:
- 確保
@Track 屬性在 UI 中合法使用
- 檢查非
@Track 屬性是否意外綁定到 UI
我是今陽(yáng),如果想要進(jìn)階和了解更多的干貨,歡迎關(guān)注微信公眾號(hào) “今陽(yáng)說(shuō)” 接收我的最新文章