在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。
一、@State修饰符
@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。当这些状态数据被修改时,将会调用所在组件的 build() 方法刷新UI。
@State 状态数据具有以下特征:
支持多种数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。支持Date类型。API11及以上支持Map、Set类型。支持undefined和null类型。API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null。
注意:当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@State a : string | undefined = undefiend是推荐的,不推荐@State a: string = undefined。
组件内部私有化:被@State标记过的成员变量只能在组件内部访问。
- 相互独立:可以重复创建多个实例对象,组件不同实例的内部数据是相互独立的,具有隔离性。
- 必须初始化:必须为所有@State变量进行初始化。
父组件代码:
import { SubItem } from './SubItem'
// StateTest.ets
@Entry
@Component
struct StateTest{
@State seconds:string = "父组件秒钟数:"+new Date().getSeconds()
build() {
Column({space:20}){
Text(this.seconds)
.backgroundColor(Color.Green)
.fontColor(Color.White)
.fontSize(18)
//两个独立的子组件
SubItem()
SubItem()
Button("点击更新")
.onClick(() => {
this.seconds = "父组件秒钟:" + new Date().getSeconds()
})
}
.width('100%')
.height('100%')
.padding(20)
}
}
子组件代码:
// SubItem.ets
//特别注意:子组件不能用@Entry注解
@Component
export struct SubItem{
@State seconds: string = "子组件秒钟:" + new Date().getSeconds()
build() {
Text(this.seconds)
.fontSize(18)
.backgroundColor(Color.Red)
.fontColor(Color.Orange)
.onClick(() => {
this.seconds = "子组件秒钟:" + new Date().getSeconds()
})
}
}
运行效果图:
二、@Prop修饰符
@Prop修饰符用于父子单向同步,是父传子,而子不改父。其特性如下:
- 内部私有:只在子组件内部有效,被@Prop修饰的变量是私有变量
- 相互独立:同一子组件可以重复创建实例,每个实例内的数据是私有的,具有隔离性
- 不支持内部初始化,必须从父组件传值进行组件化
- 支持的数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。不支持any,支持undefined和null。支持Date类型。
注意 当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@Prop a : string | undefined = undefined是推荐的,不推荐@Prop a: string = undefined。
示例代码:
@Entry
@Component
struct PropTest{
@State seconds:string = new Date().getSeconds().toString()
build() {
Column({space:20}){
Text(`父组件:${this.seconds}`)
.backgroundColor(Color.Orange)
.fontColor(Color.White)
.fontSize(20)
.padding(10)
//解构赋值给子组件
Item({date:this.seconds})
Item({date:this.seconds})
Button('更新时间')
.backgroundColor(Color.Orange)
.fontColor(Color.White)
.fontSize(20)
.onClick(() => {
this.seconds = new Date().getSeconds().toString()
})
}
.width('100%')
.height('100%')
.padding(20)
}
}
@Component
struct Item{
@Prop date:string
build() {
Text(`子组件:${this.date}`)
.backgroundColor(Color.Green)
.fontColor(Color.Orange)
.fontSize(20)
.padding(10)
.onClick(() => {
this.date = new Date().getSeconds().toString()
})
}
}
在点击“更新时间”按钮时,3个控件都呈现同样的文本结果。这说明父组件成功向子组件传递了信息。
在分别点击两个子组件时,父组件并没有变化,而两个子组件则分别做出不同的文本呈现结果。这说明,@Prop修饰的变量是私有的,并且不会影响父组件。
三、@Link修饰符
@Link修饰符用于父子双向同步。即:父组件数据的更新会通知子组件的变量更新,同时驱动UI更新。@Link修饰符主要有以下特点:
- 内部私有:只能在组件内部访问
- 只能通过父组件中的@State变量进行初始化,不能在组件内初始化
- 支持的数据类型:Object、class、string、number、boolean、enum类型,以及这些类型的数组。支持Date类型。API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null。
注意:当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@Link a : string | undefined。
@Entry
@Component
struct LinkTest{
@State seconds:string = new Date().getSeconds().toString()
build() {
Column({space:20}){
Text(`父组件:${this.seconds}`)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
.fontSize(18)
.padding(20)
// 此处使用$符号进行初始化,其实使用this.seconds也能实现效果,不知为啥
MyItem({childText: $seconds})
MyItem({childText: $seconds})
Button('更新父组件秒钟数')
.backgroundColor(Color.Red)
.fontColor(Color.White)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.seconds = new Date().getSeconds().toString()
})
}
.width('100%')
.height('100%')
.padding(50)
}
}
//子组件
@Component
struct MyItem{
// @Link修饰的变量只能被父组件初始化
@Link childText:string
build() {
Text(`子组件:${this.childText}`)
.fontColor(Color.Orange)
.fontSize(18)
.backgroundColor(Color.Green)
.padding(20)
.onClick(() => {
this.childText = new Date().getSeconds().toString()
})
}
}
代码运行效果图:
四、@Provide和@Consume:与后代组件双向同步
五、@Watch修饰符
@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。
//子组件
@Component
struct MyItem{
// @Watch修饰符监听变量的改变,当变量被改变时会执行回调方法
@Watch('onValueChange') @Link childText:string
build() {
Text(`子组件:${this.childText}`)
.fontColor(Color.Orange)
.fontSize(18)
.backgroundColor(Color.Green)
.padding(20)
.onClick(() => {
this.childText = new Date().getSeconds().toString()
})
}
onValueChange(){
console.log(`childText成员变量更新:${this.childText}`)
}