前言:
在我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面,就需要引入"状态"的概念。
ArkUI 状态管理V1
提供了多种装饰器,通过使用这些装饰器,状态变量不仅可以观察在组件内的改变,还可以在不同组件层级间传递,比如父子组件、跨组件层级,也可以观察全局范围内的变化。
从数据的传递形式和同步类型层面看,装饰器也可分为:
- 只读的单向传递;
- 可变更的双向传递。
V1的装饰器
@State
在所有的状态管理装饰器中@State
是最基础的一个,他能够赋予监听一个属性的数据状态的变化。
@State
装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就可以触发其直接绑定UI 组件的刷新。当状态改变时,UI会发生对应的渲染改变。
js
interface iHobby{
number:number;
swim:boolean;
run:boolean;
}
interface iPerson{
name:string;
age:number;
hobby:iHobby;
}
@Entry
@Component
struct Index {
@State num:number=0
@State arr:number[]=[1,2,3]
@State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
build() {
Column() {
Text('num:'+this.num)
Text('arr:'+this.arr)
Text('p1:'+JSON.stringify(this.p1))
Button('++')
.onClick(() => {
this.num++
this.arr.push(this.num)
this.p1.age++
// this.p1.hobby.number++
})
}
.width('100%')
}
}
但值得注意的是对于复杂数据类型,@State
并不能监听到深层的数据变化,只能监听浅层也就是第一层的数据变化。
js
interface iHobby{
number:number;
swim:boolean;
run:boolean;
}
interface iPerson{
name:string;
age:number;
hobby:iHobby;
}
@Entry
@Component
struct Index {
@State num:number=0
@State arr:number[]=[1,2,3]
@State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
build() {
Column() {
// Text('num:'+this.num)
// Text('arr:'+this.arr)
Text('p1:'+JSON.stringify(this.p1))
Button('++')
.onClick(() => {
// this.num++
// this.arr.push(this.num)
// this.p1.age++
this.p1.hobby.number++
})
}
.width('100%')
}
}
点击增加按钮视图没有发生任何的改变
@Prop
@Prop
一般在子组件中使用,它能够接受来自于父组件传递的数据,并且能够同步父组件的响应式变化。
但值得注意的是,它本身不具备修改父组件数据的能力,如果强行修改则只会修改子组件中的数据不会影响父组件的数据。
js
interface iHobby{
number:number;
swim:boolean;
run:boolean;
}
interface iPerson{
name:string;
age:number;
hobby:iHobby;
}
@Entry
@Component
struct Index {
@State num:number=0
@State arr:number[]=[1,2,3]
@State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
build() {
Column() {
Text('父组件num:'+this.num)
Text('父组件arr:'+this.arr)
Text('父组件name:'+JSON.stringify(this.p1))
Button('父组件++')
.onClick(() => {
this.num++
this.arr.push(this.num)
this.p1.age++
// this.p1.hobby.number++
})
Divider()
Son({
num:this.num,
arr:this.arr,
p1:this.p1
})
}
.width('100%')
}
}
@Component
struct Son {
@Prop num:number
@Prop arr:number[]
@Prop p1:iPerson
build() {
Column(){
Text('子组件num:'+this.num)
Text('子组件arr:'+this.arr)
Text('子组件name:'+JSON.stringify(this.p1))
Button('子组件++')
.onClick(()=>{
this.num++
this.arr.push(this.num)
this.p1.age++
// this.p1.hobby.number++
})
}
}
}
@Link
@Link
弥补了前文当中提到的@Prop
,子组件的修改不会引起父组件的数据变化,实现了父子组件的双向同步。
js
interface iHobby{
number:number;
swim:boolean;
run:boolean;
}
interface iPerson{
name:string;
age:number;
hobby:iHobby;
}
@Entry
@Component
struct Index {
@State num:number=0
@State arr:number[]=[1,2,3]
@State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
build() {
Column() {
Text('父组件num:'+this.num)
Text('父组件arr:'+this.arr)
Text('父组件name:'+JSON.stringify(this.p1))
Button('父组件++')
.onClick(() => {
this.num++
this.arr.push(this.num)
this.p1.age++
// this.p1.hobby.number++
})
Divider()
Son({
num:this.num,
arr:this.arr,
p1:this.p1
})
}
.width('100%')
}
}
@Component
struct Son {
@Link num:number
@Link arr:number[]
@Link p1:iPerson
build() {
Column(){
Text('子组件num:'+this.num)
Text('子组件arr:'+this.arr)
Text('子组件name:'+JSON.stringify(this.p1))
Button('子组件++')
.onClick(()=>{
this.num++
this.arr.push(this.num)
this.p1.age++
// this.p1.hobby.number++
})
}
}
}
@Provide & @Consume
@Provide
和@Consume
,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。
js
@Entry
@Component
struct Index {
@Provide num:number=0
@Provide arr:number[]=[1,2,3]
build() {
Column() {
Text('父组件num:'+this.num)
Text('父组件arr:'+this.arr)
Button('父组件++')
.onClick(() => {
this.num++
this.arr.push(this.num)
})
Divider()
Son()
}
.width('100%')
}
}
@Component
struct Grandson {
@Consume num:number
@Consume arr:number[]
@Consume p1:iPerson
build() {
Column(){
Text('孙组件num:'+this.num)
Text('孙组件arr:'+this.arr)
Button('孙组件++')
.onClick(()=>{
this.num++
this.arr.push(this.num)
})
}
}
}
@Component
struct Son {
@Consume num:number
@Consume arr:number[]
@Consume p1:iPerson
build() {
Column(){
Text('子组件num:'+this.num)
Text('子组件arr:'+this.arr)
Button('子组件++')
.onClick(()=>{
this.num++
this.arr.push(this.num)
})
Divider()
Grandson()
}
}
}
@Observed & @ObjectLink
上文所述的装饰器,仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class
,或者class
的属性是class
,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink
装饰器。
js
//【固定】1. @Observed 装饰类
@Observed
class ClassA{
// 略
}
//2. 父组件
@Component
struct ParentCom{
// 【非固定写法】根据需求来定义数据
@State 变量名: ClassA[] = [ new ClassA(),new ClassA(), ]
@State 变量名:ClassA = new ClassA()
@State 变量名: ClassA[][] = [[new ClassA(),new ClassA()],[new ClassA(),new ClassA()] ]
}
// 【固定】3. 子组件
@Component
struct ChildCom{
@ObjectLink 变量名: ClassA
}
需要对class
声明的类名使用@Observed
,对于传递的子组件使用@ObjectLink
。
js
@Observed
class iPerson {
name:string
age:number
hobby:iHobby
constructor(name:string, age:number,hobby:iHobby) {
this.name=name
this.age=age
this.hobby=hobby
}
}
class iHobby{
num:number
constructor(num:number) {
this.num=num
}
}
@Entry
@Component
struct Index {
@State p1:iPerson=new iPerson('张三',18,new iHobby(1))
build() {
Column() {
Text('name:'+this.p1.name)
Text('age:'+this.p1.age)
Text('hobby:'+this.p1.hobby.num)
Button('父组件++')
.onClick(() => {
this.p1.age++
this.p1.hobby.num++
})
Divider()
Son({
p1:this.p1,
})
}
.width('100%')
}
}
@Component
struct Son {
@ObjectLink p1:iPerson
build() {
Column(){
Text('name:'+this.p1.name)
Text('age:'+this.p1.age)
Text('hobby:'+this.p1.hobby.num)
Button('子组件++')
.onClick(()=>{
this.p1.age++
this.p1.hobby.num++
})
}
}
}