
文章目录前言一、Watch 基础用法1.1 语法结构1.2 监听数组/对象二、Watch 实战场景2.1 搜索词变化自动请求2.2 坐标变化时更新地图三、Watch 的注意事项3.1 避免无限循环3.2 Watch 回调的执行时机四、Watch 与直接处理的选择总结前言有时候我们不仅需要 UI 随状态变化而更新还需要状态变化时执行某些业务逻辑副作用——比如搜索词变化时发起网络请求、坐标变化时更新地图、选中项改变时播放提示音……ArkUI 的Watch装饰器专门用于监听状态变化 → 执行副作用本篇通过项目实战场景讲解其正确用法。一、Watch 基础用法1.1 语法结构Componentstruct WatchDemo{StateWatch(onCountChange)count:number0;// Watch 指定的回调方法状态变化时自动调用onCountChange():void{console.log(count 变化了新值${this.count});// 在这里执行副作用逻辑}build(){Column({space:16}){Text(count ${this.count})Button(1).onClick((){this.count;})}.padding(24).width(100%).justifyContent(FlexAlign.Center)}}提示Watch(methodName)写在State之后方法名是字符串对应组件中定义的方法。1.2 监听数组/对象Componentstruct WatchArrayDemo{StateWatch(onListChange)selectedIds:string[][];StatetotalSelected:number0;StatesummaryText:string未选择任何项;onListChange():void{// 列表变化时重新计算统计信息this.totalSelectedthis.selectedIds.length;if(this.totalSelected0){this.summaryText未选择任何项;}else{this.summaryText已选择${this.totalSelected}个加油站;}console.log(列表更新${JSON.stringify(this.selectedIds)});}toggleSelect(id:string):void{if(this.selectedIds.includes(id)){this.selectedIdsthis.selectedIds.filter(ii!id);}else{this.selectedIds[...this.selectedIds,id];// 触发 Watch}}build(){Column({space:12}){Text(this.summaryText).fontSize(16).fontColor(this.totalSelected0?#1A6FF5:#999999).fontWeight(FontWeight.Bold)ForEach([001,002,003,004],(id:string){Row({space:12}){Checkbox({name:id}).selectedColor(#1A6FF5).select(this.selectedIds.includes(id)).onChange(isChecked{this.toggleSelect(id);})Text(加油站${id})}.padding(12).width(100%).backgroundColor(#FFFFFF).borderRadius(8)})}.padding(20).width(100%)}}二、Watch 实战场景2.1 搜索词变化自动请求Componentstruct AutoSearchDemo{StateWatch(onSearchTextChange)searchText:string;Stateresults:string[][];StateisSearching:booleanfalse;privatesearchTimer:number-1;// 防抖搜索输入停止300ms后才发起请求onSearchTextChange():void{if(this.searchTimer!-1){clearTimeout(this.searchTimer);}if(!this.searchText.trim()){this.results[];return;}this.isSearchingtrue;this.searchTimersetTimeout((){this.doSearch(this.searchText);},300);// 300ms 防抖}asyncdoSearch(keyword:string):Promisevoid{// 模拟搜索请求awaitnewPromisevoid(resolvesetTimeout(resolve,500));constallStations[望京石化,朝阳石油,国贸壳牌,三里屯BP,道达尔望京];this.resultsallStations.filter(ss.includes(keyword));this.isSearchingfalse;}build(){Column({space:12}){TextInput({placeholder:搜索加油站...,text:this.searchText}).onChange(v{this.searchTextv;}).padding({left:16,right:16}).height(44).backgroundColor(#F0F0F0).borderRadius(22)if(this.isSearching){Row({space:8}){LoadingProgress().width(16).height(16).color(#1A6FF5)Text(搜索中...).fontSize(13).fontColor(#999999)}}elseif(this.results.length0){ForEach(this.results,(result:string){Text(result).padding(12).fontSize(15).width(100%).backgroundColor(#FFFFFF).borderRadius(8)})}elseif(this.searchText!this.isSearching){Text(未找到相关结果).fontSize(14).fontColor(#999999)}}.padding(20).width(100%)}}2.2 坐标变化时更新地图// 模拟地图页面中坐标监听Componentstruct CoordinateWatchDemo{StateWatch(onCoordinateChange)latitude:number39.9042;StateWatch(onCoordinateChange)longitude:number116.4074;StatemoveCount:number0;StatelastMoveTime:string;onCoordinateChange():void{this.moveCount;this.lastMoveTimenewDate().toLocaleTimeString();// 在实际项目中这里会调用 mapUtil.moveToCurrentPositionconsole.log(地图移动到${this.latitude.toFixed(4)},${this.longitude.toFixed(4)});}randomMove():void{// 模拟随机移动this.latitude(Math.random()-0.5)*0.01;this.longitude(Math.random()-0.5)*0.01;}build(){Column({space:16}){Text(坐标监听演示).fontSize(18).fontWeight(FontWeight.Bold)Text(纬度${this.latitude.toFixed(6)}).fontSize(14)Text(经度${this.longitude.toFixed(6)}).fontSize(14)Text(地图移动次数${this.moveCount}).fontSize(14).fontColor(#1A6FF5)if(this.lastMoveTime){Text(最后移动${this.lastMoveTime}).fontSize(12).fontColor(#999999)}Button(随机移动位置).onClick((){this.randomMove();}).backgroundColor(#1A6FF5).fontColor(#FFFFFF).borderRadius(20)}.padding(24).width(100%).justifyContent(FlexAlign.Center)}}三、Watch 的注意事项3.1 避免无限循环Componentstruct InfiniteLoopWarning{StateWatch(onAChange)a:number0;StateWatch(onBChange)b:number0;// ❌ 危险a 变化 → 修改 b → b 变化 → 修改 a → 无限循环onAChange():void{this.bthis.a1;// 修改 b触发 onBChange}onBChange():void{this.athis.b-1;// 修改 a触发 onAChange → 死循环}}// ✅ 正确使用标志位避免循环Componentstruct SafeWatchExample{StateWatch(onAChange)a:number0;Stateb:number0;// b 不加 WatchprivateisUpdating:booleanfalse;onAChange():void{if(this.isUpdating)return;// 防止重入this.isUpdatingtrue;this.bthis.a*2;// 修改 bb 没有 Watch不会触发回调this.isUpdatingfalse;}}3.2 Watch 回调的执行时机Componentstruct WatchTimingDemo{StateWatch(onValueChange)value:number0;onValueChange():void{// 注意此时 this.value 已经是新值// Watch 回调在状态赋值完成后、UI 重新渲染前执行console.log(新值${this.value});// ✅ 可以在这里修改其他 State会合并到同一次渲染// ❌ 不要做耗时操作会阻塞 UI 渲染// ✅ 异步操作可以用 setTimeout 或 async/await}build(){Button(当前值${this.value}).onClick((){this.value;})}}四、Watch 与直接处理的选择场景推荐方式点击按钮同时修改状态和执行副作用直接在onClick中处理任何来源修改状态都需要执行副作用Watch多个地方会修改同一状态Watch统一处理不遗漏副作用依赖多个状态在各自的Watch中调用同一方法// ✅ 推荐多处修改都能触发的副作用用 WatchComponentstruct WatchVsOnClick{StateWatch(onLocationUpdate)location:{lat:number,lng:number}{lat:0,lng:0};onLocationUpdate():void{// 无论是用户点击、定位回调还是外部传入修改了 location// 这里都会执行地图更新逻辑console.log(位置已更新刷新地图);}// 用户点击按钮修改位置handleManualInput(lat:number,lng:number):void{this.location{lat,lng};// 触发 Watch}// 定位回调修改位置handleLocationCallback(lat:number,lng:number):void{this.location{lat,lng};// 触发 Watch}}总结Watch是 ArkUI 响应式系统的重要补充——当你需要状态变化时执行业务逻辑而不仅仅是更新 UIWatch就是最优雅的解决方案。合理使用Watch能让状态变化和副作用的处理统一在一处避免漏处理的情况同时要注意防止循环触发以及不在回调中执行耗时同步操作。