HarmonyOS (ArkTS)状态管理
一、状态管理分为:
- 页面级变量的状态管理 (主要用于单页面,同一个页面内不同组件之间的状态管理。)
-
应用级变量的状态管理 (主要用于多页面,同一个应用内,不同页面之间的状态管理。例如:A页面和B页面实现数据共享)
1、页面级变量的状态管理
@State、@Prop、@Link、@Provide、@Consume、@ObjectLink、@Observed和@Watch用于管理页面级变量的状态。
装饰器 |
装饰内容 |
说明 |
@State |
基本数据类型,类,数组 |
修饰的状态数据被修改时会触发组件的build方法进行UI界面更新。 (只会修改当前组件的数据,不会修改子组件数据) |
@Prop
|
基本数据类
|
修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,更新当前组件的UI。
(父组件改变会同步到子组件,但子组件改变不会同步到父组件)
|
@Link |
基本数据类型,类,数组 |
父子组件之间的双向数据绑定,父组件的内部状态数据作为数据源,任何一方所做的修改都会反映给另一方。 |
@Observed |
类 |
@Observed应用于类,表示该类中的数据变更被UI页面管理。 |
@ObjectLink |
被@Observed 所装饰类的 对象 |
装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它 关联的状态数据所在的组件都会更新UI。(结合@Observed 使用) (用于多层嵌套(对象套对象,数组套对象等 最深层对象的属性改变了)的属性改变时更新UI) |
@Consume |
基本数据类 型,类,数组 |
@Consume装饰的变量在感知到@Provide装饰的变量更新 后,会触发当前自定义组件的重新渲染。 |
@Provide |
基本数据型,类,数组 |
@Provide作为数据的提供方,可以更新其子孙节点的数据,并 触发页面渲染。 |
@Watch |
基本数据型,类,数组 |
@Watch应用于对状态变量的监听。如果开发者需要关注某个 状态变量的值是否改变,可以使用@Watch为状态变量设置回 调函数 |
@State
修饰的状态数据被修改时会触发组件的build方法进行UI界面更新。
@Entry @Component struct Index { @State count:number=1 build() { Stack(){ Column() { Text(`${this.count}`) .fontSize(40) .fontWeight(FontWeight.Bold) Box({ num:this.count, color:Color.Red }) Box({ num:this.count, color:Color.Orange }) Box({ num:this.count, color:Color.Pink }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) Button({type:ButtonType.Circle}){ Text("+").fontSize(40).fontColor(Color.White) }.width(60) .height(60) .position({ x:"80%", y:"90%" }) .onClick(()=>{ this.count++ }) } } } @Component struct Box{ private color:Color private num:number build(){ Column(){ Text(`${this.num}`) .fontSize(40) .fontWeight(FontWeight.Bold) } .width(120) .height(120) .backgroundColor(this.color) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .margin({ top:10 }) } }
@Prop 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,更新当前组件的UI
@Entry @Component struct StatePropPage { @State count:number=1 build() { Stack(){ Column() { Text(`${this.count}`) .fontSize(40) .fontWeight(FontWeight.Bold) Container({ num:this.count, color:Color.Red }) Container({ num:this.count, color:Color.Orange }) Container({ num:this.count, color:Color.Pink }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) Button({type:ButtonType.Circle}){ Text("+").fontSize(40).fontColor(Color.White) }.width(60) .height(60) .position({ x:"80%", y:"90%" }) .onClick(()=>{ this.count++ }) } } } @Component struct Container{ private color:Color @Prop num:number //@Prop num和count建立一个单向绑定的关系 build(){ Column(){ Text(`${this.num}`) .fontSize(40) .fontWeight(FontWeight.Bold) } .width(120) .height(120) .backgroundColor(this.color) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .margin({ top:10 }) .onClick(()=>{ this.num++ }) } }
@Link 使用$传参 注意:传参使用$
父子组件之间的双向数据绑定,父组件的内部状态数据作为数据源,任何一方所做的修改都会反映给另一方
@Entry @Component struct StateLinkPage { @State count:number=1 build() { Stack(){ Column() { Text(`${this.count}`) .fontSize(40) .fontWeight(FontWeight.Bold) Box1({ num:$count, //注意:子组件使用@Link装饰器接收数据的话 父组件需要通过$进行传值 color:Color.Red }) Box1({ num:$count, color:Color.Orange }) Box1({ num:$count, color:Color.Pink }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) Button({type:ButtonType.Circle}){ Text("+").fontSize(40).fontColor(Color.White) }.width(60) .height(60) .position({ x:"80%", y:"90%" }) .onClick(()=>{ this.count++ }) } } } @Component struct Box1{ private color:Color @Link num:number //@Link num和count建立一个双向绑定的关系 build(){ Column(){ Text(`${this.num}`) .fontSize(40) .fontWeight(FontWeight.Bold) } .width(120) .height(120) .backgroundColor(this.color) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .margin({ top:10 }) .onClick(()=>{ this.num++ }) } }
@Provide @Consume
@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的 场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递 机制的束缚,实现跨层级传递。
Index组件调用了SecondBox组件,SecondBox组件调用了ThirdBox组件,我们要实现的功能是Index 组件和ThirdBox组件直接的数据相互绑定
@Entry @Component struct StateProvideConsumePage { @State count:number=1 build() { Stack(){ Column() { Text(`${this.count}`) .fontSize(40) .fontWeight(FontWeight.Bold) SecondBox({ num:$count, //注意:子组件使用@Link装饰器接收数据的话 父组件需要通过$进行传值 color:Color.Red }) SecondBox({ num:$count, color:Color.Orange }) SecondBox({ num:$count, color:Color.Pink }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) Button({type:ButtonType.Circle}){ Text("+").fontSize(40).fontColor(Color.White) }.width(60) .height(60) .position({ x:"80%", y:"90%" }) .onClick(()=>{ this.count++ }) } } } @Component struct SecondBox{ private color:Color @Link num:number //@Link num和count建立一个双向绑定的关系 build(){ Column(){ ThirdBox({ num:$num }) } .width(120) .height(120) .backgroundColor(this.color) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .margin({ top:10 }) } } @Component struct ThirdBox{ @Link num:number build(){ Text(`${this.num}`) .fontSize(40) .fontWeight(FontWeight.Bold) .width(120) .height(120) .textAlign(TextAlign.Center) .onClick(()=>{ console.log(`this.num=${this.num}`) this.num++ }) } }
@Watch
@Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数
@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。
@Watch可用于购物车计算总价,或者实现计算器功能等
@Entry @Component struct StateWatchPage { @State @Watch("change") count:number=1 @State @Watch("change") pow:number=2 @State res:number=1 change(){ this.res=Math.pow(this.count,this.pow) } build() { Column() { Row(){ Text('基数:' ) .fontSize(20) TextInput({ text:this.count.toString() }) .type(InputType.Number) .layoutWeight(1) .height(40) .borderRadius(10) .onChange((value)=>{ if(value==null || value==undefined || value==""){ this.count=0 }else{ this.count=parseInt(value) } }) }.margin({ bottom:20 }) Row(){ Text('次幂:') .fontSize(20) TextInput({ text:this.pow.toString() }).type(InputType.Number) .layoutWeight(1) .height(40) .borderRadius(10) .onChange((value)=>{ if(value==null || value==undefined || value==""){ this.pow=0 }else{ this.pow=parseInt(value) } }) }.margin({ bottom:20 }) Row(){ Text("结果:" + this.res) .fontSize(30) } } .width('100%') .height('100%') .padding(20) } }
应用级变量的状态管理
AppStorage结合 @StorageProp @StorageLink装饰器
AppStorage包含整个应用程序中需要访问的所有状态属性,只要应用程序保持运行,AppStorage就会 保存所有属性及属性值,属性值可以通过唯一的键值进行访问。
组件可以通过装饰器将应用程序状态数据与AppStorage进行同步,应用业务逻辑的实现也可以通过接 口访问AppStorage。
AppStorage的选择状态属性可以与不同的数据源或数据接收器同步,这些数据源和接收器可以是设备 上的本地或远程,并具有不同的功能,如数据持久性。这样的数据源和接收器可以独立于UI在业务逻辑中实现 。
@StorageProp装饰器
组件通过使用@StorageProp(key)装饰的状态变量,将与AppStorage建立单向数据绑定,key标识 AppStorage中的属性键值。当创建包含@StoageProp的状态变量的组件时,该状态变量的值将使用 AppStorage中的值进行初始化。AppStorage中的属性值的更改会导致绑定的UI组件进行状态更新。
import router from '@ohos.router' @Entry @Component struct StateStorePropAppStoragePage { @StorageLink("num") num:number=10 @StorageProp("count") count:number=1 build() { Column() { Text(`count=${this.count}`).fontSize(30) .margin({ top:20 }) Button(){ Text(`设置StorageProp值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ AppStorage.Set<number>("count",AppStorage.Get<number>("count")+1) }).margin({ top:20 }) Button(){ Text(`获StorageProp取值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ let tempCount=AppStorage.Get<number>("count") console.log(tempCount.toString()) }).margin({ top:20 }) Button(){ Text(`改变变量Count的值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ this.count++ }).margin({ top:20 }) Text(`num=${this.num}`).fontSize(30) .margin({ top:100 }) Button(){ Text(`设置num的值-News页面会同步状态`).fontSize(18).fontColor(Color.White) }.width("96%") .height(40) .onClick(()=>{ ++this.num PersistentStorage.PersistProp("num",this.num) }).margin({ top:20 }) Button(){ Text(`跳转到news`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ router.pushUrl({ url:"pages/News" }) }).margin({ top:20 }) }.height('100%') .width("100%") } }
@StorageLink装饰器
组件通过使用@StorageLink(key)装饰的状态变量,与AppStorage建立双向数据绑定,key为 AppStorage中的属性键值。当创建包含@StorageLink的状态变量的组件时,该状态变量的值将使用 AppStorage中的值进行初始化。在UI组件中对@StorageLink的状态变量所做的更改将同步到 AppStorage,并从AppStorage同步到任何其他绑定实例中,如PersistentStorage或其他绑定的UI组件。
@Entry @Component struct StateStoreLinkAppStoragePage { @StorageLink("count") count:number=1 build() { Column() { Text(`${this.count}`).fontSize(30) .margin({ top:20 }) Button(){ Text(`设置值-StorageLink`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ AppStorage.Set<number>("count",AppStorage.Get<number>("count")+1) }).margin({ top:20 }) Button(){ Text(`获取值-StorageLink`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ let tempCount=AppStorage.Get<number>("count") console.log(tempCount.toString()) }).margin({ top:20 }) Button(){ Text(`改变变量Count的值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ this.count++ }).margin({ top:20 }) }.height('100%') .width("100%") } }
AppStorage PersistentStorage
PersistentStorage类提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链 接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问 对应key的变量。
AppStorage PersistentStorage结合使用可以实现不同页面之间的数据共享。
A页面设置数据 B页面获取数据
A页面
Button(){ Text("设置值")fontColor(Color.White) }.width("90%") .height(40) .onClick(()=>{ PersistentStorage.PersistProp("count",this.count) }).margin({ top:20 })
B页面
Button(){ Text("获取值")fontColor(Color.White) }.width("90%") .height(40) .onClick(()=>{ let count = AppStorage.Get<number>("count") console.log(`${count}`) }).margin({ top:20 })
import { AppBar } from "../widget/AppBar" import router from '@ohos.router' @Component export struct Home { @StorageLink("num") num:number=10 @StorageProp("count") count:number=1 build() { Column() { AppBar({ title: "首页" }) Text(`count=${this.count}`).fontSize(30) .margin({ top:20 }) Button(){ Text(`设置StorageProp值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ AppStorage.Set<number>("count",AppStorage.Get<number>("count")+1) }).margin({ top:20 }) Button(){ Text(`获StorageProp取值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ let tempCount=AppStorage.Get<number>("count") console.log(tempCount.toString()) }).margin({ top:20 }) Button(){ Text(`改变变量Count的值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ this.count++ }).margin({ top:20 }) Text(`num=${this.num}`).fontSize(30) .margin({ top:100 }) Button(){ Text(`设置num的值-News页面会同步状态`).fontSize(18).fontColor(Color.White) }.width("96%") .height(40) .onClick(()=>{ ++this.num PersistentStorage.PersistProp("num",this.num) }).margin({ top:20 }) Button(){ Text(`跳转到news`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ router.pushUrl({ url:"pages/News" }) }).margin({ top:20 }) }.height('100%') .width("100%") } }
import { AppBar } from "./widget/AppBar" @Entry @Component struct News { @StorageLink("num") num:number=0 build() { Column() { AppBar({ title: "新闻" }) Text(`num=${this.num}`) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ top:20 }) Button(){ Text(`设置num的值`).fontSize(18).fontColor(Color.White) }.width(200) .height(40) .onClick(()=>{ ++this.num PersistentStorage.PersistProp("num",this.num) }).margin({ top:20 }) } .width('100%') .height('100%') } }