news 2026/6/9 10:51:25

HarmonyOS ArkUI @Watch 装饰器完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS ArkUI @Watch 装饰器完全指南

文章目录

    • 前言
    • 一、@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{@State@Watch('onCountChange')count:number=0;// @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{@State@Watch('onListChange')selectedIds:string[]=[];@StatetotalSelected:number=0;@StatesummaryText:string='未选择任何项';onListChange():void{// 列表变化时重新计算统计信息this.totalSelected=this.selectedIds.length;if(this.totalSelected===0){this.summaryText='未选择任何项';}else{this.summaryText=`已选择${this.totalSelected}个加油站`;}console.log(`列表更新:${JSON.stringify(this.selectedIds)}`);}toggleSelect(id:string):void{if(this.selectedIds.includes(id)){this.selectedIds=this.selectedIds.filter(i=>i!==id);}else{this.selectedIds=[...this.selectedIds,id];// 触发 @Watch}}build(){Column({space:12}){Text(this.summaryText).fontSize(16).fontColor(this.totalSelected>0?'#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{@State@Watch('onSearchTextChange')searchText:string='';@Stateresults:string[]=[];@StateisSearching:boolean=false;privatesearchTimer:number=-1;// 防抖搜索:输入停止300ms后才发起请求onSearchTextChange():void{if(this.searchTimer!==-1){clearTimeout(this.searchTimer);}if(!this.searchText.trim()){this.results=[];return;}this.isSearching=true;this.searchTimer=setTimeout(()=>{this.doSearch(this.searchText);},300);// 300ms 防抖}asyncdoSearch(keyword:string):Promise<void>{// 模拟搜索请求awaitnewPromise<void>(resolve=>setTimeout(resolve,500));constallStations=['望京石化','朝阳石油','国贸壳牌','三里屯BP','道达尔望京'];this.results=allStations.filter(s=>s.includes(keyword));this.isSearching=false;}build(){Column({space:12}){TextInput({placeholder:'搜索加油站...',text:this.searchText}).onChange(v=>{this.searchText=v;}).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.length>0){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{@State@Watch('onCoordinateChange')latitude:number=39.9042;@State@Watch('onCoordinateChange')longitude:number=116.4074;@StatemoveCount:number=0;@StatelastMoveTime:string='';onCoordinateChange():void{this.moveCount++;this.lastMoveTime=newDate().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{@State@Watch('onAChange')a:number=0;@State@Watch('onBChange')b:number=0;// ❌ 危险:a 变化 → 修改 b → b 变化 → 修改 a → 无限循环!onAChange():void{this.b=this.a+1;// 修改 b,触发 onBChange}onBChange():void{this.a=this.b-1;// 修改 a,触发 onAChange → 死循环!}}// ✅ 正确:使用标志位避免循环@Componentstruct SafeWatchExample{@State@Watch('onAChange')a:number=0;@Stateb:number=0;// b 不加 @WatchprivateisUpdating:boolean=false;onAChange():void{if(this.isUpdating)return;// 防止重入this.isUpdating=true;this.b=this.a*2;// 修改 b(b 没有 @Watch,不会触发回调)this.isUpdating=false;}}

3.2 @Watch 回调的执行时机

@Componentstruct WatchTimingDemo{@State@Watch('onValueChange')value:number=0;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中调用同一方法
// ✅ 推荐:多处修改都能触发的副作用用 @Watch@Componentstruct WatchVsOnClick{@State@Watch('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 响应式系统的重要补充——当你需要"状态变化时执行业务逻辑"(而不仅仅是更新 UI),@Watch就是最优雅的解决方案。合理使用@Watch能让状态变化和副作用的处理统一在一处,避免漏处理的情况;同时要注意防止循环触发,以及不在回调中执行耗时同步操作。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 10:44:56

Qt项目里集成NI-VISA库踩坑实录:从找不到头文件到成功读取示波器数据

Qt项目集成NI-VISA库实战指南&#xff1a;从环境配置到仪器通信当我们需要在Qt项目中与数字示波器、万用表等测试仪器通信时&#xff0c;NI-VISA库往往是绕不开的关键组件。然而&#xff0c;从官方文档到网络教程&#xff0c;很少有完整介绍如何将VISA库无缝集成到Qt开发环境中…

作者头像 李华
网站建设 2026/6/9 10:43:09

STM8S103F3上用C语言实现LED控制与EV1527遥控信号解码的实操工程

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;这个工程是基于STM8S103F3单片机的可直接运行的C语言项目&#xff0c;重点实现两个功能&#xff1a;一是通过GPIO控制LED按指定节奏闪烁&#xff0c;二是接收并解析EV1527编码芯片发出的无线遥控信号&#xff0…

作者头像 李华
网站建设 2026/6/9 10:43:08

Umi-OCR:构建本地化文字识别工作流的开源解决方案

Umi-OCR&#xff1a;构建本地化文字识别工作流的开源解决方案 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片&#xff0c;PDF文档识别&#xff0c;排除水印/页眉页脚&#xff0c;扫描/生成二维码。内置多国语言库。…

作者头像 李华
网站建设 2026/6/9 10:42:53

【C++拷贝构造与赋值重载】C++拷贝构造与赋值重载终极精讲:默认拷贝、自定义拷贝、浅拷贝深拷贝原理、自赋值判断、资源泄漏与工程解决方案

0. 前言在C面向对象开发中&#xff0c;对象的复制与赋值是最基础、也是最容易出致命BUG的核心场景。相比于普通变量赋值&#xff0c;类对象包含堆内存资源、文件句柄、指针成员等复杂资源&#xff0c;简单的赋值复制往往会引发内存泄漏、野指针、重复释放、数据错乱、程序崩溃等…

作者头像 李华
网站建设 2026/6/9 10:42:52

计算机毕业设计之基于Hadoop的小说网站设计

随着当代信息科学技术的飞速发展&#xff0c;在现代的信息社会中&#xff0c;人们经济水平也逐渐提高&#xff0c;传统线下管理方式等方面遇到了瓶颈&#xff0c;有些系统虽然在用户需求功能上加入了高科技的体验&#xff0c;然而在酷炫的高科技身后&#xff0c;将会带来高昂的…

作者头像 李华
网站建设 2026/6/9 10:41:05

算法不确定性对专家决策的影响:大学录取实证研究

1. 算法不确定性如何影响专家决策&#xff1f;选择性大学录取的实证研究在当今数据驱动的决策环境中&#xff0c;算法预测系统正日益渗透到高等教育录取等高风险领域。作为一位长期关注教育技术与决策科学的从业者&#xff0c;我最近深入研读了斯坦福大学和康奈尔大学团队关于算…

作者头像 李华