@Watch狀態監聽機制:響應式數據變化處理
文章簡介
在HarmonyOS應用開發中,狀態管理是構建響應式應用的核心。@Watch裝飾器作為ArkUI的重要特性,能夠監聽狀態變量的變化並執行相應的回調函數。本文將深入講解@Watch的使用方法、原理和最佳實踐。
官方參考資料:
- @Watch裝飾器
一、@Watch基礎概念
1.1 什麼是@Watch裝飾器
@Watch是ArkTS語言中的裝飾器,用於監聽狀態變量的變化。當被裝飾的狀態變量發生改變時,@Watch裝飾的方法會被自動調用。
核心特性:
- 響應式數據監聽
- 自動觸發回調函數
- 支持同步和異步操作
- 可監聽多個狀態變量
1.2 基本語法結構
@State @Watch('onCountChange') count: number = 0;
onCountChange(): void {
// 當count變化時執行
console.log('Count changed to: ' + this.count);
}
二、@Watch裝飾器詳解
2.1 裝飾器參數説明
@Watch裝飾器接受一個字符串參數,指定要調用的回調方法名。
參數配置表:
|
參數
|
類型
|
必填
|
説明
|
|
callback
|
string
|
是
|
狀態變化時調用的方法名
|
|
-
|
-
|
-
|
方法必須為無參數或單參數形式
|
2.2 支持的數據類型
@Watch可以監聽多種數據類型的狀態變化:
- 基本類型:number、string、boolean
- 對象類型:class實例、interface實現
- 數組類型:Array
- 嵌套對象:多層嵌套的數據結構
三、@Watch實戰應用
3.1 基本使用示例
@Component
struct WatchBasicExample {
@State @Watch('onUserNameChange') userName: string = 'HarmonyOS';
@State logMessages: string[] = [];
onUserNameChange(): void {
this.logMessages.push(`用户名變為: ${this.userName}`);
}
build() {
Column() {
TextInput({ placeholder: '請輸入用户名' })
.onChange((value: string) => {
this.userName = value;
})
Text('當前用户名: ' + this.userName)
.fontSize(20)
.margin(10)
// 顯示變化日誌
ForEach(this.logMessages, (message: string) => {
Text(message)
.fontSize(14)
.fontColor(Color.Gray)
})
}
.padding(20)
}
}
3.2 監聽多個狀態變量
@Component
struct MultipleWatchExample {
@State @Watch('onFormChange') firstName: string = '';
@State @Watch('onFormChange') lastName: string = '';
@State @Watch('onFormChange') age: number = 0;
@State fullName: string = '';
@State formChangeCount: number = 0;
onFormChange(): void {
this.fullName = `${this.firstName} ${this.lastName}`;
this.formChangeCount++;
console.log(`表單第${this.formChangeCount}次變化:`);
console.log(`- 姓: ${this.firstName}`);
console.log(`- 名: ${this.lastName}`);
console.log(`- 年齡: ${this.age}`);
}
build() {
Column() {
TextInput({ placeholder: '姓' })
.onChange((value: string) => {
this.firstName = value;
})
TextInput({ placeholder: '名' })
.onChange((value: string) => {
this.lastName = value;
})
Slider({
value: this.age,
min: 0,
max: 100
})
.onChange((value: number) => {
this.age = value;
})
Text(`全名: ${this.fullName}`)
.fontSize(18)
.margin(10)
Text(`表單變化次數: ${this.formChangeCount}`)
.fontSize(14)
.fontColor(Color.Blue)
}
.padding(20)
}
}
3.3 對象屬性監聽
class UserProfile {
name: string = '';
email: string = '';
level: number = 1;
}
@Component
struct ObjectWatchExample {
@State @Watch('onProfileChange') profile: UserProfile = new UserProfile();
@State changeHistory: string[] = [];
onProfileChange(): void {
const timestamp = new Date().toLocaleTimeString();
this.changeHistory.push(`${timestamp}: ${this.profile.name} - ${this.profile.email}`);
// 限制歷史記錄數量
if (this.changeHistory.length > 5) {
this.changeHistory = this.changeHistory.slice(-5);
}
}
build() {
Column() {
TextInput({ placeholder: '姓名' })
.onChange((value: string) => {
this.profile.name = value;
})
TextInput({ placeholder: '郵箱' })
.onChange((value: string) => {
this.profile.email = value;
})
Text('變更歷史:')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
ForEach(this.changeHistory, (history: string) => {
Text(history)
.fontSize(12)
.fontColor(Color.Gray)
})
}
.padding(20)
}
}
四、@Watch高級用法
4.1 條件觸發與防抖處理
@Component
struct DebounceWatchExample {
@State @Watch('onSearchKeywordChange') searchKeyword: string = '';
@State searchResults: string[] = [];
@State isLoading: boolean = false;
// 模擬搜索函數(帶防抖)
onSearchKeywordChange(): void {
if (this.searchKeyword.length < 2) {
this.searchResults = [];
return;
}
this.isLoading = true;
// 模擬API調用延遲
setTimeout(() => {
this.performSearch();
}, 300);
}
performSearch(): void {
// 模擬搜索結果
this.searchResults = [
`結果1: ${this.searchKeyword}相關`,
`結果2: ${this.searchKeyword}教程`,
`結果3: ${this.searchKeyword}示例`
];
this.isLoading = false;
}
build() {
Column() {
TextInput({ placeholder: '輸入關鍵詞搜索...' })
.onChange((value: string) => {
this.searchKeyword = value;
})
if (this.isLoading) {
LoadingProgress()
.color(Color.Blue)
}
ForEach(this.searchResults, (result: string) => {
Text(result)
.fontSize(14)
.margin(5)
})
}
.padding(20)
}
}
4.2 數組變化監聽
@Component
struct ArrayWatchExample {
@State @Watch('onTodoListChange') todoList: string[] = [];
@State completedCount: number = 0;
@State totalCount: number = 0;
onTodoListChange(): void {
this.totalCount = this.todoList.length;
console.log(`待辦事項數量: ${this.totalCount}`);
}
addTodoItem(): void {
const newItem = `任務 ${this.todoList.length + 1}`;
this.todoList.push(newItem);
// 需要重新賦值觸發監聽
this.todoList = [...this.todoList];
}
removeTodoItem(index: number): void {
this.todoList.splice(index, 1);
this.todoList = [...this.todoList];
}
build() {
Column() {
Button('添加任務')
.onClick(() => {
this.addTodoItem();
})
Text(`總任務數: ${this.totalCount}`)
.fontSize(16)
.margin(10)
ForEach(this.todoList, (item: string, index?: number) => {
Row() {
Text(item)
.fontSize(14)
Button('刪除')
.onClick(() => {
this.removeTodoItem(index!);
})
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.margin(5)
})
}
.padding(20)
}
}
五、@Watch與其它裝飾器配合
5.1 與@Link配合使用
@Component
struct ParentComponent {
@State parentCount: number = 0;
build() {
Column() {
Text(`父組件計數: ${this.parentCount}`)
.fontSize(20)
Button('父組件增加')
.onClick(() => {
this.parentCount++;
})
ChildComponent({ count: $parentCount })
}
.padding(20)
}
}
@Component
struct ChildComponent {
@Link @Watch('onCountChange') count: number;
@State changeLog: string[] = [];
onCountChange(): void {
this.changeLog.push(`計數變化: ${this.count}`);
}
build() {
Column() {
Text(`子組件計數: ${this.count}`)
.fontSize(18)
Button('子組件增加')
.onClick(() => {
this.count++;
})
ForEach(this.changeLog, (log: string) => {
Text(log)
.fontSize(12)
.fontColor(Color.Green)
})
}
.padding(15)
.backgroundColor(Color.White)
}
}
5.2 與@Prop和@Provide/@Consume配合
@Entry
@Component
struct WatchWithProvideExample {
@Provide @Watch('onThemeChange') theme: string = 'light';
onThemeChange(): void {
console.log(`主題切換為: ${this.theme}`);
}
build() {
Column() {
Text('主題設置')
.fontSize(24)
Button('切換主題')
.onClick(() => {
this.theme = this.theme === 'light' ? 'dark' : 'light';
})
ThemeConsumerComponent()
}
.width('100%')
.height('100%')
}
}
@Component
struct ThemeConsumerComponent {
@Consume theme: string;
build() {
Column() {
Text(`當前主題: ${this.theme}`)
.fontSize(18)
.fontColor(this.theme === 'light' ? Color.Black : Color.White)
}
.width('100%')
.height(200)
.backgroundColor(this.theme === 'light' ? Color.White : Color.Black)
}
}
六、性能優化與最佳實踐
6.1 避免不必要的監聽
@Component
struct OptimizedWatchExample {
@State essentialData: string = '';
@State nonEssentialData: string = '';
// 只監聽必要的數據
@State @Watch('onEssentialChange') criticalValue: number = 0;
onEssentialChange(): void {
// 只處理關鍵業務邏輯
this.performCriticalOperation();
}
performCriticalOperation(): void {
// 關鍵業務操作
console.log('執行關鍵操作...');
}
build() {
Column() {
// 界面構建...
}
}
}
6.2 批量更新策略
@Component
struct BatchUpdateExample {
@State @Watch('onDataUpdate') dataSet: number[] = [1, 2, 3];
@State updateCount: number = 0;
onDataUpdate(): void {
this.updateCount++;
console.log(`數據更新次數: ${this.updateCount}`);
}
// 批量更新方法
batchUpdateData(): void {
const newData = [...this.dataSet];
// 多次修改
newData.push(4);
newData.push(5);
newData[0] = 100;
// 一次性賦值,只觸發一次@Watch
this.dataSet = newData;
}
build() {
Column() {
Text(`更新次數: ${this.updateCount}`)
.fontSize(18)
Button('批量更新')
.onClick(() => {
this.batchUpdateData();
})
ForEach(this.dataSet, (item: number) => {
Text(`數據: ${item}`)
.fontSize(14)
})
}
.padding(20)
}
}
七、常見問題與解決方案
7.1 @Watch不觸發的常見原因
問題排查清單:
- 狀態變量未使用
@State裝飾 - 回調方法名拼寫錯誤
- 對象引用未改變(需創建新對象)
- 數組操作後未重新賦值
- 在構造函數中修改狀態
7.2 循環監聽問題
@Component
struct CircularWatchExample {
@State @Watch('onValueAChange') valueA: number = 0;
@State @Watch('onValueBChange') valueB: number = 0;
// 錯誤的循環監聽
onValueAChange(): void {
// 這會導致循環調用!
// this.valueB = this.valueA * 2;
}
onValueBChange(): void {
// 這也會導致循環調用!
// this.valueA = this.valueB / 2;
}
// 正確的解決方案
safeUpdateB(): void {
this.valueB = this.valueA * 2;
}
build() {
Column() {
// 界面構建...
}
}
}
八、版本兼容性説明
8.1 API版本要求
|
HarmonyOS版本
|
@Watch支持
|
備註
|
|
4.0.0+
|
✅ 完全支持
|
推薦使用
|
|
3.1.0-3.2.0
|
✅ 基本支持
|
部分高級特性不可用
|
|
3.0.0及以下
|
❌ 不支持
|
需使用其他狀態管理方案
|
8.2 開發環境配置
重要提示: 確保開發環境滿足以下要求:
- DevEco Studio 4.0+
- SDK API 10+
- 編譯工具鏈最新版本
拓展學習推薦
@Monitor裝飾器 v2狀態管理中可以使用@Monitor更加優化的實現類似效果