当前位置: 首页 > 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.cnnetsun.cn/news/3356081.html

相关文章:

  • 鸿蒙原生开发手记:徒步迹 - @Provide/@Consume 跨组件通信
  • 微信小程序活体人脸检测实战包(含可运行源码+界面截图+调用说明)
  • 抖音批量下载终极指南:如何用douyin-downloader高效管理你的创作素材库
  • AI绘画实战:用Stable Diffusion将文字描述转化为魔法少女OC变身图
  • 数据科学家不是安全威胁,而是未被防护的工作流节点
  • 用Python构建可配置AI人格系统
  • JS逆向工程实战:从抓包分析到AI辅助参数加密破解
  • Unity游戏开发:从Animator到HFSM的状态管理进阶指南
  • 秒开!用浏览器直接唤醒 Bambu Studio 打印模型
  • 【claude code实践】让 Claude Code 修改前端页面:组件、状态与样式协同
  • 如何零门槛打造你的B站专属视频库:从大会员4K到充电专属内容的全攻略
  • NCMDump:解锁网易云音乐加密格式的简易工具
  • 企业网盘权限模型解析:多层级访问控制与审计能力选型指南
  • PyQt+OpenCV图像处理教学工具:10+算法可视化实操包,含GUI界面与测试图
  • Unity性能优化全攻略:从CPU/GPU瓶颈分析到移动端与VR实战
  • Unity调用Android NDK动态库全流程:从原理到实战避坑指南
  • Linux chsh命令详解:修改用户默认Shell的完整指南
  • AI与数字孪生驱动的智能空调控制系统实践
  • 大模型多步推理,能力衰减会逐步衰减影响效果?该怎么办!
  • Unity水体渲染实战:从Gerstner波到屏幕空间反射的完整实现
  • 考虑火-储联合调频(火电机组-混合储能)的协同控制策略(Matlab代码实现)
  • 基于Godot与C#的工业3D可视化上位机开发实战
  • 钓鱼装备系统配置指南:从鱼竿渔轮到线组调漂的实战选择
  • 如何免费解锁Wand专业版:3种简单方法获得完整游戏修改功能
  • PaperXie AI:学术PPT智能生成工具解析与应用
  • MATLAB一键运行PCA+SVM分类全流程:含数据预处理、降维可视化与预测结果图
  • 英雄联盟Akari助手:5分钟快速上手的免费自动化工具完整指南
  • Faster R-CNN与Res2Net-101在野火烟雾检测中的应用
  • C++ Boost搜索引擎核心:正排与倒排索引架构设计与实现
  • Docker部署Oracle 12c:从镜像拉取到生产连接全流程实战