鸿蒙原生开发手记:徒步迹 - @State/@Prop/@Link 状态管理 掌握 ArkUI 装饰器体系构建响应式 UI一、前言状态管理是声明式 UI 框架的核心。ArkUI 提供了一套装饰器体系让开发者声明式地定义组件状态和依赖关系框架自动追踪状态变化并更新 UI。本文将详解 State、Prop、Link 三个最基础也最重要的装饰器。二、状态管理概览ArkUI 装饰器按作用范围分为三层装饰器作用范围用途State组件内部组件私有状态Prop父→子父组件向子组件传数据Link父子双向父子组件双向同步Provide跨级传递祖先→后代提供者Consume跨级传递后代→祖先消费者Watch状态监听监听状态变化回调ObjectLink对象属性监听对象属性级变化三、State - 组件私有状态3.1 基本用法Componentstruct StepCounter{StatestepCount:number0;// 状态变量StateisRecording:booleanfalse;StaterouteName:string;build(){Column(){Text(步数:${this.stepCount})// 自动响应变化.fontSize(20);Button(加1).onClick((){this.stepCount;// 状态变化UI 自动更新});Button(this.isRecording?记录中:开始记录).backgroundColor(this.isRecording?#FF6F00:#4CAF50).onClick((){this.isRecording!this.isRecording;});}}}3.2 State 的深层变化监听Componentstruct RouteDisplay{Stateroute:HikingRoute{id:1,name:香山徒步,distance:8.5,tags:[森林,山景],};build(){Column(){Text(this.route.name).fontSize(18);// 修改对象属性 - State 会自动监听深层变化Button(修改名称).onClick((){this.route{...this.route,name:新路线名,// 重新赋值触发更新};});}}}注意State 监听的是赋值操作修改对象属性时需要重新赋值才会触发更新。对于数组需要使用新数组替换。四、Prop - 父传子Prop 用于父组件向子组件传递数据是单向数据流// 子组件 - 接收父组件传入的数据Componentstruct RouteCard{ProprouteName:string;// 从父组件接收Propdistance:number0;Propdifficulty:string简单;build(){Column(){Text(this.routeName).fontSize(16).fontWeight(FontWeight.Bold);Text(距离:${this.distance}km).fontSize(14).fontColor(#666);Text(难度:${this.difficulty}).fontSize(12).fontColor(this.getDifficultyColor());}.padding(12).backgroundColor(#FFF).borderRadius(8);}getDifficultyColor():ResourceColor{if(this.difficulty简单)return#4CAF50;if(this.difficulty中等)return#FF9800;return#F44336;}}// 父组件 - 传入数据EntryComponentstruct RouteListPage{build(){Column(){RouteCard({routeName:香山徒步,distance:8.5,difficulty:中等,});}.padding(16);}}Prop 的限制子组件不能修改 Prop 的值只读父组件数据变化时Prop 自动同步更新Prop 是深拷贝不会影响父组件原始数据五、Link - 父子双向同步Link 实现父子组件间的数据双向同步// 子组件Componentstruct RecordingControl{LinkWatch(onStatusChanged)status:string;// 双向绑定Linkduration:number;onStatusChanged():void{console.log(状态变更为:${this.status});}build(){Column(){// 子组件修改 Link 变量 → 父组件同步更新Button(this.statusrecording?暂停:继续).onClick((){this.statusthis.statusrecording?paused:recording;});Text(时长:${this.duration}s);}}}// 父组件EntryComponentstruct TrackingPage{StaterecordingStatus:stringidle;StaterecordingDuration:number0;build(){Column(){Text(状态:${this.recordingStatus}).fontSize(16);// 使用 $ 前缀传递 Link 引用RecordingControl({status:$recordingStatus,duration:$recordingDuration,});}}}$ 语法使用$变量名将 State 变量的引用传递给 Link// 父组件传递ChildComponent({propVar:this.stateVar,// Prop: 传值linkVar:$stateVar,// Link: 传引用})六、实战徒步迹状态管理结合徒步迹的轨迹记录页面实际演示状态管理的使用// TrackingPage.etsEntryComponentstruct TrackingPage{StateisRecording:booleanfalse;StateisPaused:booleanfalse;Stateduration:number0;Statedistance:number0;privatetimerId:number-1;build(){Column(){// 统计数据区域Row(){StatItem({label:时长,value:this.formatTime(this.duration)});StatItem({label:距离,value:${this.distance.toFixed(2)}});StatItem({label:配速,value:this.calcPace()});}.width(100%).padding(16);// 控制按钮区域Row(){if(!this.isRecording){Button(开始徒步).width(180).height(56).backgroundColor(#4CAF50).borderRadius(28).onClick(()this.startRecording());}else{Button(this.isPaused?继续:暂停).width(72).height(72).backgroundColor(this.isPaused?#4CAF50:#FF9800).borderRadius(36).onClick((){if(this.isPaused)this.resumeRecording();elsethis.pauseRecording();});Button(停止).width(72).height(72).backgroundColor(#F44336).borderRadius(36).margin({left:40}).onClick(()this.stopRecording());}}.padding({bottom:40});}.width(100%).height(100%);}startRecording():void{this.isRecordingtrue;this.timerIdsetInterval((){this.duration;this.distance0.005;},1000);}pauseRecording():void{this.isPausedtrue;clearInterval(this.timerId);}resumeRecording():void{this.isPausedfalse;this.startTimer();}stopRecording():void{this.isRecordingfalse;this.isPausedfalse;clearInterval(this.timerId);// 重置数据this.duration0;this.distance0;}startTimer():void{this.timerIdsetInterval((){this.duration;this.distance0.005;},1000);}formatTime(seconds:number):string{constmMath.floor(seconds/60);constsseconds%60;return${m.toString().padStart(2,0)}:${s.toString().padStart(2,0)};}calcPace():string{if(this.distance0)return0\00;constpacethis.duration/this.distance;return${Math.floor(pace/60)}${Math.floor(pace%60).toString().padStart(2,0)};}}Componentstruct StatItem{Proplabel:string;Propvalue:string;build(){Column(){Text(this.value).fontSize(24).fontWeight(FontWeight.Bold);Text(this.label).fontSize(13).fontColor(#666);}.alignItems(HorizontalAlign.Center);}}七、State 最佳实践最小化状态只保留需要 UI 响应的变量避免冗余能从其他状态推导的值不要单独声明类型明确始终为 State 变量提供初始值不可变性尽量使用只读接口// 好 - 状态最小化Statedistance:number0;// 只需存储基础数据// 配速由 duration 和 distance 计算得出不需要额外状态// 不好 - 状态冗余Statedistance:number0;Statepace:string0\00;// 可以从 distance/duration 推导八、总结本文详细介绍了 ArkUI 状态管理的三个核心装饰器State组件私有、Prop父传子、Link双向同步。理解它们的区别和适用场景是构建正确响应式 UI 的关键。下一篇文章将学习跨组件通信的 Provide/Consume 装饰器。下一篇预告鸿蒙原生开发手记徒步迹 - Provide/Consume 跨组件通信