news 2026/7/26 11:42:04

HarmonyOs应用《日记本》开发第9篇 - @State 状态管理详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOs应用《日记本》开发第9篇 - @State 状态管理详解

状态管理是声明式 UI 框架的核心。在 ArkUI 中,@State是最基础的状态管理装饰器,它让变量变为"响应式"——当变量值改变时,UI 会自动更新。本篇将通过日记项目的实际代码,深入解析@State的工作原理和最佳实践。

@State 基本概念

什么是响应式状态

@Statediaries:DiaryItem[]=[];@StateisLoading:boolean=true;

@State装饰的变量具有以下特性:

  1. 响应式:值变化时,引用该变量的 UI 自动重新渲染
  2. 本地性:状态属于当前组件,不与其他组件共享
  3. 初始化:必须提供初始值

没有 @State 的世界

如果不用@State

// ❌ 不使用 @Statestruct Index{diaries:DiaryItem[]=[];// 普通变量isLoading:boolean=true;asyncloadData():Promise<void>{this.diaries=awaitdiaryStore.getAllDiaries();// 数据变了,但 UI 不会更新!// 用户看到的还是空列表或加载中状态}}

使用@State

// ✅ 使用 @Statestruct Index{@Statediaries:DiaryItem[]=[];// 响应式变量@StateisLoading:boolean=true;asyncloadData():Promise<void>{this.isLoading=true;// UI 自动更新为加载状态this.diaries=awaitdiaryStore.getAllDiaries();// UI 自动更新为列表this.isLoading=false;// UI 自动隐藏加载状态}}

日记项目中的 @State 使用

Index 页面的状态

@Entry@Componentstruct Index{@Statediaries:DiaryItem[]=[];// 日记列表数据@StateisLoading:boolean=true;// 加载状态aboutToAppear():void{this.loadData();}onPageShow():void{this.loadData();}asyncloadData():Promise<void>{this.isLoading=true;// → UI 切换到加载中try{this.diaries=awaitdiaryStore.getAllDiaries();// → UI 切换到列表}catch(e){console.error('loadData error: '+JSON.stringify(e));}this.isLoading=false;// → 隐藏加载动画}build():void{Column(){this.HeaderBar()if(this.isLoading){// isLoading 为 true 时显示Column(){LoadingProgress().width(40).height(40).color('#FF6B6B')Text('加载中...').fontSize(14).fontColor('#999')}}elseif(this.diaries.length===0){// isLoading 为 false 且列表为空时显示this.EmptyView()}else{// isLoading 为 false 且列表有数据时显示this.DiaryList()}}}}

状态变化驱动的 UI 切换

isLoading = true isLoading = false diaries = [] diaries = [item1, item2, ...] │ │ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ │ │ ┌──────────────┐ │ │ 加载中... │ ───────→ │ │ 日记卡片 1 │ │ │ ⟳ │ │ ├──────────────┤ │ │ │ │ │ 日记卡片 2 │ │ └──────────────┘ │ └──────────────┘ │ └──────────────────┘

DiaryEdit 页面的状态

@Entry@Componentstruct DiaryEdit{@Statetitle:string='';// 标题@Statecontent:string='';// 内容@Statedate:string='';// 日期@Statemood:string='calm';// 心情@Stateweather:string='sunny';// 天气@StatediaryId:string='';// 日记ID@StateisEdit:boolean=false;// 是否编辑模式@Statesaving:boolean=false;// 保存中状态@StateselectedDate:Date=newDate();// 日期选择器选中值}

状态与 UI 的绑定关系

// 标题输入 → @State titleTextInput({text:this.title,placeholder:'给今天起个标题吧...'}).onChange((value:string)=>{this.title=value;// 状态更新 → 输入框保持同步})// 心情选择 → @State moodForEach(MoodList,(item:MoodWeatherItem)=>{Column(){Text(item.emoji).fontSize(28)Text(item.label).fontColor(this.mood===item.key?'#FF6B6B':'#999')// mood 变化时,选中颜色自动更新}.backgroundColor(this.mood===item.key?'#FFF0F0':'#FFF')// mood 变化时,背景色自动更新.border(this.mood===item.key?{width:1.5,color:'#FF6B6B'}:{width:0,color:'#FFF'}).onClick(()=>{this.mood=item.key;// 点击切换心情 → UI 自动更新选中态})})// 保存按钮 → @State savingText('保存').opacity(this.saving?0.5:1)// 保存中时半透明

DiaryDetail 页面的状态

@Entry@Componentstruct DiaryDetail{@Statediary:DiaryItem|null=null;// 日记数据@Stateloading:boolean=true;// 加载状态@StateshowDeleteDialog:boolean=false;// 删除弹窗显示privatediaryId:string='';// 普通变量,不驱动 UI}

注意diaryId没有用@State装饰,因为它不直接驱动 UI 渲染,只是内部使用的标识。

删除弹窗的状态控制

build():void{Column(){this.NavBar()if(this.loading){// 加载中}elseif(this.diary){this.DetailContent()}else{// 日记不存在}}// 弹窗绑定到 showDeleteDialog 状态.bindContentCover($$this.showDeleteDialog,this.DeleteDialog())}@BuilderDeleteDialog():void{Column(){// ...Text('取消').onClick(()=>{this.showDeleteDialog=false;// 关闭弹窗})Text('删除').onClick(()=>{this.showDeleteDialog=false;// 关闭弹窗this.deleteDiary();// 执行删除})}}

@State 的观察机制

基本类型观察

@StateisLoading:boolean=true;// 赋值触发 UI 更新this.isLoading=false;// ✅ 触发更新this.isLoading=true;// ✅ 触发更新

对象类型观察

@Statediary:DiaryItem|null=null;// 整体替换触发更新this.diary=newDiaryItem(...);// ✅ 触发更新// 属性修改的观察行为this.diary.title='新标题';// ⚠️ 取决于框架实现

数组类型观察

@Statediaries:DiaryItem[]=[];// 整体替换触发更新this.diaries=newArray;// ✅ 触发更新// 数组方法this.diaries.push(newItem);// ✅ 触发更新this.diaries.splice(0,1);// ✅ 触发更新this.diaries.filter(...);// ⚠️ 返回新数组,需要重新赋值

在日记项目中,loadData方法通过整体替换更新数组:

asyncloadData():Promise<void>{this.diaries=awaitdiaryStore.getAllDiaries();// 整体替换}

$$ 双向绑定语法

日记详情页中使用了$$语法:

.bindContentCover($$this.showDeleteDialog,this.DeleteDialog())

$$是双向绑定操作符,用于将@State变量与组件的内置状态双向同步:

  • showDeleteDialog变为true→ 弹窗显示
  • 弹窗内部关闭 →showDeleteDialog变为false

@State 的初始化

必须提供初始值

// ✅ 正确:有初始值@Statetitle:string='';@StateisLoading:boolean=true;@Statediary:DiaryItem|null=null;@Statediaries:DiaryItem[]=[];// ❌ 错误:缺少初始值@Statetitle:string;@StateisLoading:boolean;

合理的默认值

日记项目中的默认值选择很有讲究:

@Statemood:string='calm';// 默认平静心情@Stateweather:string='sunny';// 默认晴天@StateisEdit:boolean=false;// 默认新建模式@Statesaving:boolean=false;// 默认非保存状态@StateselectedDate:Date=newDate();// 默认今天

状态管理最佳实践

1. 最小化状态

// ✅ 好的做法:只保留必要的 @State@Statediary:DiaryItem|null=null;@Stateloading:boolean=true;@StateshowDeleteDialog:boolean=false;privatediaryId:string='';// 不驱动 UI,不用 @State

2. 状态与计算值分离

// 不要为能计算出来的值创建 @State@Statediaries:DiaryItem[]=[];// ✅ 计算值直接在 build 中计算if(this.diaries.length===0){this.EmptyView()}else{this.DiaryList()}

3. 状态更新与错误处理

asyncloadData():Promise<void>{this.isLoading=true;try{this.diaries=awaitdiaryStore.getAllDiaries();}catch(e){console.error('loadData error: '+JSON.stringify(e));// 错误时也更新状态,避免一直加载中}this.isLoading=false;// 无论成功失败都关闭加载状态}

4. 防抖与竞态

asyncsaveDiary():Promise<void>{if(this.saving)return;// 防止重复提交this.saving=true;constsuccess=awaitdiaryStore.saveDiary(diary);this.saving=false;if(success){setTimeout(()=>{router.back();},500);}}

小结

@State是 ArkUI 状态管理的基石,通过日记项目的实践,我们掌握了:

  1. 响应式原理:@State 变量变化自动触发 UI 更新
  2. 状态设计:只将驱动 UI 的变量标记为 @State
  3. 类型支持:基本类型、对象类型、数组类型
  4. 双向绑定$$语法用于组件内置状态同步
  5. 最佳实践:最小化状态、合理默认值、竞态处理
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/26 11:41:32

如何用20个关键词高效吸收4小时技术分享:从信息过载到知识内化

在实际技术分享和知识管理场景中&#xff0c;如何高效地整理、串联和消化长达数小时的深度分享内容&#xff0c;是一个普遍存在的挑战。无论是参加行业技术大会、内部培训&#xff0c;还是学习线上大师课&#xff0c;面对海量信息点&#xff0c;缺乏有效的方法论和工具&#xf…

作者头像 李华
网站建设 2026/7/26 11:40:44

TMS320C2x DSP加载指令深度解析:从LACL到LAR的汇编优化实践

1. 指令集架构与加载指令的核心价值 在嵌入式数字信号处理&#xff08;DSP&#xff09;领域&#xff0c;尤其是德州仪器&#xff08;TI&#xff09;的TMS320C2x系列处理器上&#xff0c;汇编编程是榨干硬件性能、实现确定性实时响应的不二法门。与高级语言不同&#xff0c;汇编…

作者头像 李华
网站建设 2026/7/26 11:40:13

Cortile工作区管理终极指南:多桌面高效切换技巧

Cortile工作区管理终极指南&#xff1a;多桌面高效切换技巧 【免费下载链接】cortile Linux auto tiling manager with hot corner support for Openbox, Fluxbox, IceWM, Xfwm, KWin, Marco, Muffin, Mutter and other EWMH compliant window managers using the X11 window …

作者头像 李华
网站建设 2026/7/26 11:39:18

国外陶土板施工案例分析

国外陶土板施工案例分析 2010年10月,炎热的迪拜夏季刚过去,温度还在35度以上,空气中酷暑的味道还没退去,随着土建主体建筑的完成,建筑幕墙的施工就正式展开,对计划的要求(或者说节点的控制),是国外项目比较明显的特点。结合这个项目中比较有特色的陶土板幕墙案例,讨论…

作者头像 李华
网站建设 2026/7/26 11:36:13

数字墓碑技术:二维码与云存储实现生命记忆数字化

1. 数字墓碑技术概述 数字墓碑是传统墓碑与现代数字技术相结合的创新产物&#xff0c;通过在墓碑上嵌入二维码&#xff0c;将逝者的生平信息、影像资料等内容数字化存储。当访客扫描二维码时&#xff0c;即可通过移动设备观看视频、浏览照片、阅读生平介绍等多媒体内容。这种技…

作者头像 李华