当前位置: 首页 > news >正文

鸿蒙原生开发手记:徒步迹 - @Watch 与 @ObjectLink 深入理解

掌握状态监听和对象级响应,提升 ArkUI 状态管理能力


一、前言

ArkUI 的状态管理体系中,除了基础的 @State/@Prop/@Link 和跨级通信的 @Provide/@Consume,还有两个重要的装饰器:@Watch 和 @ObjectLink。

  • @Watch:监听状态变化,执行回调函数
  • @ObjectLink:监听对象的属性级变化,避免不必要的重新渲染

二、@Watch - 状态监听

2.1 基本用法

@Watch 用于监听 @State 变量的变化,变化时自动执行回调:

@Componentstruct DistanceTracker{@State@Watch('onDistanceChanged')distance:number=0;@State@Watch('onStatusChanged')status:string='idle';// @Watch 回调:每次 distance 变化时触发onDistanceChanged():void{console.log(`距离更新:${this.distance}km`);this.checkMilestone();}onStatusChanged():void{console.log(`状态变更:${this.status}`);}checkMilestone():void{if(this.distance>=10&&this.distance<10.01){console.log('🎉 达到10公里里程碑!');}}build(){Column(){Text(`距离:${this.distance.toFixed(2)}km`);Button('模拟前进').onClick(()=>{this.distance+=0.5;// 触发 @Watch 回调});Button('停止').onClick(()=>{this.status='completed';// 触发 @Watch 回调});}}}

2.2 与 @Link/@Prop 配合

@Componentstruct ChildComponent{@Link@Watch('onValueChange')sharedValue:number;onValueChange():void{console.log(`子组件检测到值变化:${this.sharedValue}`);// 可以在此执行数据验证、副作用等}}

2.3 @Watch 与计算属性

由于 ArkTS 不支持计算属性(getter),可以用 @Watch 模拟:

@Componentstruct RouteStats{@State@Watch('calcDerivedData')distance:number=0;@State@Watch('calcDerivedData')duration:number=0;@Statepace:string='0\'00"';// 派生状态@StateavgSpeed:number=0;// 派生状态calcDerivedData():void{// 当 distance 或 duration 变化时,重新计算派生数据if(this.duration>0&&this.distance>0){this.pace=this.formatPace(this.duration,this.distance);this.avgSpeed=this.distance/(this.duration/3600);}}formatPace(seconds:number,km:number):string{constpacePerKm=seconds/km;constmin=Math.floor(pacePerKm/60);constsec=Math.floor(pacePerKm%60);return`${min}'${sec.toString().padStart(2,'0')}"`;}build(){Column(){Text(`配速:${this.pace}`);Text(`均速:${this.avgSpeed.toFixed(1)}km/h`);}}}

三、@ObjectLink - 对象属性级监听

3.1 解决的问题

默认情况下,@State 监听的是对象引用变化,而不是属性变化:

// ❌ 以下方式不会触发 UI 更新@Stateroute={name:'香山',distance:5};this.route.distance=10;// 属性修改,UI 不更新// ✅ 需要重新赋值this.route={...this.route,distance:10};

@ObjectLink 解决了这个问题,可以监听对象属性的变化。

3.2 使用 @ObjectLink

// 定义类(需要使用 @Observed 装饰)@ObservedclassTrackRecord{name:string='';distance:number=0;duration:number=0;points:Coordinate[]=[];constructor(name:string){this.name=name;}}// 父组件@Componentstruct TrackDetail{@Staterecord:TrackRecord=newTrackRecord('晨间徒步');build(){Column(){// 传入 @ObjectLinkTrackInfo({record:this.record});Button('更新距离').onClick(()=>{// @ObjectLink 可以监听到属性变化!this.record.distance+=1;});}}}// 子组件 - 使用 @ObjectLink 接收@Componentstruct TrackInfo{@ObjectLinkrecord:TrackRecord;build(){Column(){Text(`名称:${this.record.name}`);Text(`距离:${this.record.distance.toFixed(1)}km`);Text(`用时:${this.record.duration}s`);}}}

3.3 @Observed 装饰器

使用 @ObjectLink 前,类必须用@Observed装饰:

@ObservedclassCoordinate{latitude:number=0;longitude:number=0;altitude:number=0;}@ObservedclassTrackPoint{coord:Coordinate=newCoordinate();heartRate:number=0;timestamp:number=0;}

四、实战:徒步迹实时轨迹追踪

结合 @ObjectLink 和 @Watch,实现轨迹追踪的实时更新:

@ObservedclassTrackSession{name:string='';startTime:number=0;distance:number=0;duration:number=0;calories:number=0;points:Coordinate[]=[];}@Componentstruct TrackingController{@ObjectLinksession:TrackSession;@StatetimerId:number=-1;start():void{this.session.startTime=Date.now();this.timerId=setInterval(()=>{this.session.duration++;this.session.distance+=0.005;// 模拟轨迹点letpoint=newCoordinate();point.latitude=39.908+Math.random()*0.01;point.longitude=116.397+Math.random()*0.01;this.session.points.push(point);},1000);}stop():void{clearInterval(this.timerId);}build(){Column(){Button('开始追踪').onClick(()=>this.start());Button('停止').onClick(()=>this.stop());}}}@Componentstruct TrackingDisplay{@ObjectLinksession:TrackSession;build(){Column(){Text(`距离:${session.distance.toFixed(2)}km`);Text(`时长:${session.duration}s`);Text(`点数:${session.points.length}`);}}}@Entry@Componentstruct TrackingPage{@StatetrackSession:TrackSession=newTrackSession();build(){Column(){Text('轨迹记录').fontSize(20).fontWeight(FontWeight.Bold);TrackingDisplay({session:this.trackSession});TrackingController({session:this.trackSession});}.width('100%').height('100%').padding(16);}}

五、@Watch 与 @ObjectLink 对比

特性@Watch@ObjectLink
用途监听变化,执行副作用监听属性级变化,更新 UI
使用位置状态变量上子组件参数接收
依赖搭配 @State/@Link/@Prop搭配 @Observed 类
触发时机变量赋值时对象属性变化时
典型场景数据持久化、日志记录表单、实时数据展示

六、性能优化建议

  1. 避免频繁 @Watch 回调中的复杂计算
  2. @ObjectLink 适用于大型对象,小型对象用 @Prop 即可
  3. 合理使用 @Observed 范围,不要过度装饰
// ✅ 好:在 @Watch 中做轻量操作@Watch('saveState')onStateChange():void{AppStorage.set('lastDistance',this.distance);}// ❌ 不好:在 @Watch 中做耗时操作onStateChange():void{this.heavyComputation();// 避免this.syncToServer();// 避免}

七、总结

@Watch 和 @ObjectLink 是 ArkUI 状态管理的高级特性。@Watch 让你在状态变化时执行自定义逻辑,@ObjectLink 让你以更自然的方式修改对象属性。合理运用这些工具,可以构建出响应迅速、代码优雅的鸿蒙应用。

第一阶段(1-10篇)到此结束,下一阶段我们将进入"徒步迹"核心页面的实战开发。


下一篇预告:鸿蒙原生开发手记:徒步迹 - 应用生命周期与AbilityStage

http://www.jsqmd.com/news/1184211/

相关文章:

  • 数据科学家不是安全威胁,而是未被防护的工作流节点
  • Unity 跨平台退出方案:编辑器与运行时的一体化设计
  • 【万字文档+源码】基于SpringBoot+Vue企业绩效考核管理系统-可用于毕设-课程设计-练手学习-学习资料分享
  • AI生成PPT工具实测与高效使用技巧
  • 3分钟搞定视频字幕提取?这款本地神器让你告别繁琐转录!
  • Android Studio入门级加法计算器实战工程包,含完整UI与逻辑代码
  • 【Springboot毕设全套源码+文档】基于springbootWeJob招聘网站的设计与实现(丰富项目+远程调试+讲解+定制)
  • 2026年能做海外智能会议解决方案的公司大揭秘 - 品牌排行榜
  • 【JavaScript】WebSocket:从握手到心跳,构建高可用实时应用
  • musl C库:轻量高效的Linux系统开发与静态链接实践指南
  • 从预训练到小样本:自监督学习如何重塑数据稀缺时代的模型泛化
  • MATLAB R2021a瞬时功率法APF仿真工程包(含操作视频+多组实测效果截图)
  • 飞书AI vs 钉钉AI vs 企业微信AI:国内办公AI三强争霸
  • 大模型多步推理,能力衰减会逐步衰减影响效果?该怎么办!
  • AI技术学习Skill开发指南:从概念到实战部署
  • JOJO飙马野郎动画化:西部冒险与替身战斗的视觉革新
  • 沈阳雨水收集水池厂家选型要点及场景落地实用指南 - 热点品牌推荐
  • USD/JPY与GBP/USD分钟级价格预测模型(LSTM训练完成,开箱即用)
  • 端侧模型量化实战:INT8/FP16 与 ONNX Runtime 的移动部署
  • Binwalk固件逆向分析:从原理到实战的自动化解包指南
  • Unity D3D11设备重置错误:GPU失联的深度诊断与根治方案
  • 模板驱动型文档自动化:结构化模板与动态渲染实战指南
  • 模板驱动型文档自动化:结构化生成与组织级知识沉淀
  • 【Springboot毕设全套源码+文档】基于SpringBoot出差报销系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • Windows Docker Desktop 配置与实战:从零搭建一站式开发环境
  • Matplotlib图表可读性提升实战速查指南
  • 提示工程已成过去!Loop‑Engineering 权威指南:实现向自主代理系统的范式跃迁
  • UE5.7程序化植被系统:PCG框架下的高效自然场景构建指南
  • Poppins字体完全指南:免费多语言几何字体的终极应用教程
  • 【Springboot毕设全套源码+文档】基于SpringBoot的勤工助学系统的设计与实现(丰富项目+远程调试+讲解+定制)