移动端开发,最重要的一点就是数据的处理,并且正确的显示渲染UI。
变量在页面和组件、组件和组件之间有时候并不能实时共享,而有时候,又不需要太多的作用域(节省资源),作用就需要根据不同场景,设置不同状态的变量。
官方文档
一、状态管理概述
在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。
简单理解就是:当变量改变时,相关的UI也能跟着改变,为达到这个目的,引入了状态变量
自定义组件拥有变量,变量必须被装饰器装饰才可以成为状态变量,状态变量的改变会引起UI的渲染刷新。如果不使用状态变量,UI只能在初始化时渲染,后续将不会再刷新。 下图展示了State和View(UI)之间的关系。
因为
常规变量:没有状态的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。
所以引入状态变量
状态变量:被状态装饰器装饰的变量,改变会引起UI的渲染更新。
State:状态,一般指的是装饰器装饰的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。
二、 状态管理总览 管理应用拥有的状态
1.管理组件拥有的状态
Components级别的状态管理
装饰器 | 描述 |
---|---|
@State | @State装饰的变量拥有其所属组件的状态,可以作为其子组件单向和双向同步的数据源。当其数值改变时,会引起相关组件的渲染刷新。 |
@Prop | @Prop装饰的变量可以和父组件建立单向同步关系,@Prop装饰的变量是可变的,但修改不会同步回父组件。 |
@Link | @Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。 |
@Provide/@Consume | @Provide/@Consume装饰的变量用于跨组件层级(多层组件)同步状态变量,可以不需要通过参数命名机制传递,通过alias(别名)或者属性名绑定。 |
@Observed | @Observed装饰class,需要观察多层嵌套场景的class需要被@Observed装饰。单独使用@Observed没有任何作用,需要和@ObjectLink、@Prop连用。 |
@ObjectLink | @ObjectLink装饰的变量接收@Observed装饰的class的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。 |
2.管理应用拥有的状态
- AppStorage是应用程序中的一个特殊的单例LocalStorage对象,是应用级的数据库,和进程绑定,通过@StorageProp和@StorageLink装饰器可以和组件联动。
- AppStorage是应用状态的“中枢”,需要和组件(UI)交互的数据存入AppStorage,比如持久化数据PersistentStorage和环境变量Environment。UI再通过AppStorage提供的装饰器或者API接口,访问这些数据;
- 框架还提供了LocalStorage,AppStorage是LocalStorage特殊的单例。LocalStorage是应用程序声明的应用状态的内存“数据库”,通常用于页面级的状态共享,通过@LocalStorageProp和@LocalStorageLink装饰器可以和UI联动。
三、状态介绍
1、@State装饰器:组件内状态
@State装饰器:组件内状态
示例
@Entry
@Component
struct MyComponent {@State count: number = 0;build() {Button(`click times: ${this.count}`).onClick(() => {this.count += 1;})}
}
2、@Prop装饰器:父子单向同步
@Prop装饰器:父子单向同步
示例
- @Prop customCounter没有本地初始化,所以需要父组件提供数据源去初始化@Prop,并当父组件的数据源变化时,@Prop也将被更新;
- @Prop customCounter2有本地初始化,在这种情况下,@Prop依旧允许但非强制父组件同步数据源给@Prop。
@Component
struct MyComponent {@Prop customCounter: number;@Prop customCounter2: number = 5;build() {Column() {Row() {Text(`From Main: ${this.customCounter}`).width(90).height(40).fontColor('#FF0010')}Row() {Button('Click to change locally !').width(180).height(60).margin({ top: 10 }).onClick(() => {this.customCounter2++})}.height(100).width(180)Row() {Text(`Custom Local: ${this.customCounter2}`).width(90).height(40).fontColor('#FF0010')}}}
}
@Entry
@Component
struct MainProgram {@State mainCounter: number = 10;build() {Column() {Row() {Column() {Button('Click to change number').width(480).height(60).margin({ top: 10, bottom: 10 }).onClick(() => {this.mainCounter++})}}Row() {Column()// customCounter必须从父组件初始化,因为MyComponent的customCounter成员变量缺少本地初始化;此处,customCounter2可以不做初始化。MyComponent({ customCounter: this.mainCounter })// customCounter2也可以从父组件初始化,父组件初始化的值会覆盖子组件customCounter2的本地初始化的值MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })}}}
}
3、@Link装饰器:父子双向同步
@Link装饰器:父子双向同步
示例解释:
父组件定了了两个变量(@State greenButtonState,@State yellowButtonProp),传递给子组件,子组件使用@Link装饰器关联,当这两个变量在父组件改变时,子组件里面的值也跟着改变从而改变UI,同时由于@Link的关系,这两个参数在子组件里面改变了,也会夫组件里的变量也会跟着改变
class GreenButtonState {width: number = 0;constructor(width: number) {this.width = width;}
}
@Component
struct GreenButton {@Link greenButtonState: GreenButtonState;build() {Button('Green Button').width(this.greenButtonState.width).height(150.0).backgroundColor('#00ff00').onClick(() => {if (this.greenButtonState.width < 700) {// 更新class的属性,变化可以被观察到同步回父组件this.greenButtonState.width += 125;} else {// 更新class,变化可以被观察到同步回父组件this.greenButtonState = new GreenButtonState(100);}})}
}
@Component
struct YellowButton {@Link yellowButtonState: number;build() {Button('Yellow Button').width(this.yellowButtonState).height(150.0).backgroundColor('#ffff00').onClick(() => {// 子组件的简单类型可以同步回父组件this.yellowButtonState += 50.0;})}
}
@Entry
@Component
struct ShufflingContainer {@State greenButtonState: GreenButtonState = new GreenButtonState(300);@State yellowButtonProp: number = 100;build() {Column() {// 简单类型从父组件@State向子组件@Link数据同步Button('Parent View: Set yellowButton').onClick(() => {this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;})// class类型从父组件@State向子组件@Link数据同步Button('Parent View: Set GreenButton').onClick(() => {this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;})// class类型初始化@LinkGreenButton({ greenButtonState: $greenButtonState })// 简单类型初始化@LinkYellowButton({ yellowButtonState: $yellowButtonProp })}}
}
4、@Provide装饰器和@Consume装饰器:与后代组件双向同步
@Provide装饰器和@Consume装饰器:与后代组件双向同步
示例:
在下面的示例是与后代组件双向同步状态@Provide和@Consume场景。当分别点击CompA和CompD组件内Button时,reviewVotes 的更改会双向同步在CompA和CompD中。
@Component
struct CompD {// @Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的@Provide装饰的变量@Consume reviewVotes: number;build() {Column() {Text(`reviewVotes(${this.reviewVotes})`)Button(`reviewVotes(${this.reviewVotes}), give +1`).onClick(() => this.reviewVotes += 1)}.width('50%')}
}
@Component
struct CompC {build() {Row({ space: 5 }) {CompD()CompD()}}
}
@Component
struct CompB {build() {CompC()}
}
@Entry
@Component
struct CompA {// @Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件@Provide reviewVotes: number = 0;build() {Column() {Button(`reviewVotes(${this.reviewVotes}), give +1`).onClick(() => this.reviewVotes += 1)CompB()}}
}
5、@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
6、LocalStorage:页面级UI状态存储
LocalStorage:页面级UI状态存储
7、AppStorage:应用全局的UI状态存储
AppStorage:应用全局的UI状态存储
8、PersistentStorage:持久化存储UI状态
PersistentStorage:持久化存储UI状态
9、Environment:设备环境查询
Environment:设备环境查询
10、@Watch装饰器:状态变量更改通知
@Watch装饰器:状态变量更改通知
11、$$语法:内置组件双向同步
$$语法:内置组件双向同步