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

鸿蒙原生开发手记:徒步迹 - @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:number=0;// 状态变量@StateisRecording:boolean=false;@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:number=0;@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';}}// 父组件 - 传入数据@Entry@Componentstruct RouteListPage{build(){Column(){RouteCard({routeName:'香山徒步',distance:8.5,difficulty:'中等',});}.padding(16);}}

@Prop 的限制

  • 子组件不能修改 @Prop 的值(只读)
  • 父组件数据变化时,@Prop 自动同步更新
  • @Prop 是深拷贝,不会影响父组件原始数据

五、@Link - 父子双向同步

@Link 实现父子组件间的数据双向同步:

// 子组件@Componentstruct RecordingControl{@Link@Watch('onStatusChanged')status:string;// 双向绑定@Linkduration:number;onStatusChanged():void{console.log(`状态变更为:${this.status}`);}build(){Column(){// 子组件修改 @Link 变量 → 父组件同步更新Button(this.status==='recording'?'暂停':'继续').onClick(()=>{this.status=this.status==='recording'?'paused':'recording';});Text(`时长:${this.duration}s`);}}}// 父组件@Entry@Componentstruct TrackingPage{@StaterecordingStatus:string='idle';@StaterecordingDuration:number=0;build(){Column(){Text(`状态:${this.recordingStatus}`).fontSize(16);// 使用 $ 前缀传递 @Link 引用RecordingControl({status:$recordingStatus,duration:$recordingDuration,});}}}

$ 语法

使用$变量名将 @State 变量的引用传递给 @Link:

// 父组件传递ChildComponent({propVar:this.stateVar,// @Prop: 传值linkVar:$stateVar,// @Link: 传引用})

六、实战:徒步迹状态管理

结合"徒步迹"的轨迹记录页面,实际演示状态管理的使用:

// TrackingPage.ets@Entry@Componentstruct TrackingPage{@StateisRecording:boolean=false;@StateisPaused:boolean=false;@Stateduration:number=0;@Statedistance:number=0;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.isRecording=true;this.timerId=setInterval(()=>{this.duration++;this.distance+=0.005;},1000);}pauseRecording():void{this.isPaused=true;clearInterval(this.timerId);}resumeRecording():void{this.isPaused=false;this.startTimer();}stopRecording():void{this.isRecording=false;this.isPaused=false;clearInterval(this.timerId);// 重置数据this.duration=0;this.distance=0;}startTimer():void{this.timerId=setInterval(()=>{this.duration++;this.distance+=0.005;},1000);}formatTime(seconds:number):string{constm=Math.floor(seconds/60);consts=seconds%60;return`${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;}calcPace():string{if(this.distance<=0)return'0\'00"';constpace=this.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 最佳实践

  1. 最小化状态:只保留需要 UI 响应的变量
  2. 避免冗余:能从其他状态推导的值不要单独声明
  3. 类型明确:始终为 @State 变量提供初始值
  4. 不可变性:尽量使用只读接口
// 好 - 状态最小化@Statedistance:number=0;// 只需存储基础数据// 配速由 duration 和 distance 计算得出,不需要额外状态// 不好 - 状态冗余@Statedistance:number=0;@Statepace:string='0\'00"';// 可以从 distance/duration 推导

八、总结

本文详细介绍了 ArkUI 状态管理的三个核心装饰器:@State(组件私有)、@Prop(父传子)、@Link(双向同步)。理解它们的区别和适用场景,是构建正确响应式 UI 的关键。

下一篇文章将学习跨组件通信的 @Provide/@Consume 装饰器。


下一篇预告:鸿蒙原生开发手记:徒步迹 - @Provide/@Consume 跨组件通信

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

相关文章:

  • 基于深度学习的音频分离与OCR弹幕识别技术实战
  • CentOS 7.7 + Ubuntu 18.04 双系统引导修复与GRUB2管理实战
  • Java调R语言?Rserve这把双刃剑,一个连接就能让代码飞起来
  • Windows Server 2012 Build 7968 SKU特性解析与安装实践
  • 如何智能管理Windows文件管理器中的顽固图标问题?
  • 遗传算法实战进阶:破解早熟收敛与动态平衡调控
  • 基于Claude Fable 5与OpenRouter构建生产级AI Agent框架Damon
  • Linux 动态链接库(.so)的编译、链接与运行时路径解析实战
  • AI应用推理成本优化:从Fable 30%成本占比到实战解决方案
  • 2026年厦门钨钢螺栓厂家产品及配套服务体验测评 - 热点品牌推荐
  • 2026年采购碳化硅耐磨管 筛选靠谱厂商实用参考指南 - 热点品牌推荐
  • MATLAB版RBF手写数字识别工具包:含特征提取、预训练模型与多张测试图
  • 从瑞利商上下界到谱聚类:一个特征值边界的实践指南
  • AI如何解决毕业论文写作痛点:选题到格式全流程优化
  • Latent Diffusion Model环境配置避坑指南:从零到一搭建你的AI绘画工作站
  • 2026实力之选:成都办公室天花板源头厂家专业解析 - 甄选服务推荐
  • AI SaaS开发必须绕过的8个技术雷区:基于37个已上线项目的故障日志深度反推
  • ROS2客户端库rclpy与rclcpp深度解析:线程、QoS与实时性本质
  • 2012版ISO质量管理系统ASP部署包(含仪器台账、校准提醒与状态标识功能)
  • 微信小游戏云开发实战:从零构建云函数与数据存储
  • 2026年7月最新哈尔滨芝柏官方售后客服服务电话及地址网点大全 - 亨得利官方服务中心
  • 泊松图像编辑 OpenCV 4.8 实战:5种融合模式对比与 Python 代码实现
  • AI原创角色创作:从提示词工程到一致性管理的完整指南
  • 储能 EMS 拓扑架构设计:单机、集群、微网多场景架构对比
  • 相对价格强度RPS指标:原理、实现与实战应用指南
  • JavaWeb健身房管理系统:从零搭建MVC架构实战教程
  • 雅典官方服务项目及价格查询|详细地址与售后服务电话权威信息通告(2026年7月最新) - 亨得利钟表维修中心
  • Windows 98界面定制:仿XP与Win7视觉效果完整实现方案
  • 鸿蒙原生开发手记:徒步迹 - @Provide/@Consume 跨组件通信
  • ROS2 QoS配置实战:可靠性、持久性与生命周期策略详解