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

鸿蒙原生开发手记:徒步迹 - @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