场景 | 装饰器 | 是否需要本地初始化 | 数据类型 |
---|---|---|---|
组件内的状态管理 | @State | 是 | any,监听不到数组和对象嵌套复杂数据类型内的变化 |
从父组件单向同步状态 | @Prop | 否 | 简单数据类型 |
与父组件双向同步状态 | @Link | 否 | 简单数据类型,及其对象和数组,不接受数组和对象嵌套复杂数据类型(监听不到内部变化) |
跨组件层级双向同步状态 | @Provide和@Consume | @Provide需要 | 简单数据类型,及其对象和数组,不接受数组和对象嵌套复杂数据类型(监听不到内部变化) |
监听状态变化 | @Watch | 必须绑定方法 | |
数组对象或嵌套对象的双向数据同步 | @Observed和@ObjectLink | @objectLink修饰过的对象必须通过组件构造函数初始化 | 数组和对象嵌套复杂数据类型,需用class声明被嵌套对象,监听不到数组和对象中复杂数据类型的内部变化 |
1.组件内的状态管理 @State
@State修饰的 变量需初始化
type objType ={
id:number;
type:string;
msg:string
}
@Entry
@Component
struct Index {
// 简单数据类型 string,boolean,number
@State fatherStr: string = '这是在父组件中初始化的字符串'
// 复杂数据类型 对象,数组
// 对象 对象内属性为简单数据类型
@State fatherObj:objType ={
id:1,
type:'prop',
msg:'对象中的属性发生改变能监听到吗?'
}
// 数组 数组内元素为简单数据类型
@State fatherArr:string[]=['prop','父传子','单向']
// 数组、对象 嵌套复杂数据类型
@State fatherArrObj:objType[]=[
{
id:1,
type:'prop',
msg:'fatherArrObj?'
},
{
id:2,
type:'link',
msg:'fatherArrObj?'
}
]
build() {
Scroll(){
Column(){
Text('这是入口组件')
.fontSize(32)
// 父组件的内容
Button('父组件点击后触发改变:') .margin(12)
.onClick(()=>{
this.fatherStr = '父组件改变后,子组件会更新吗?'
this.fatherObj.msg = '对象中的属性在父组件改变'
this.fatherArr[0] = '从父改了'
this.fatherArrObj[0].msg ='数组中的对象发生了改变吗?'//并没有触发页面UI刷新
})
Text(this.fatherStr)
Text(this.fatherObj.msg)
List(){
ForEach(this.fatherArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
}
List(){
ForEach(this.fatherArrObj,(item:objType)=>{
ListItem(){
Text(item.msg)
}
},item=> item.id)
} .margin(12)
}
.backgroundColor(Color.Orange)
.padding(12)
}
}}
2.从父组件单向同步状态 @Prop——单向 父传子
子组件只展示
父组件中的状态
父组件通过 @State修饰符 定义变量,子组件通过 @Prop修饰符 获取变量。
可以修饰的类型
string、number、boolean、enum类型
父组件对象类型,子组件是对象属性
不可以是数组、any
Prop是 「单向传递」
,父组件将变量「拷贝」一份交给子组件使用,子组件不可修改变量。
父组件:
// 声明变量
@State count:number = 1
// 传值
Son({count:this.count})
子组件:
@Prop 修饰的变量不能 初始化
// 接收
@Prop count:number
// @Prop count:number = 0 会报错
示例
@Entry
@Component
struct Index {
// 简单数据类型 string,boolean,number 的组件间传递方式
@State fatherStr: string = '这是在父组件中初始化的字符串'
// 复杂数据类型 对象,数组 的组件间传递方式
// 对象 对象内属性为简单数据类型
@State fatherObj:objType ={
id:1,
type:'prop',
msg:'对象中的属性发生改变能监听到吗?'
}
// 数组 数组内元素为简单数据类型
@State @Watch('isWatching') fatherArr:string[]=['prop','父传子','单向']
// 配合@watch使用
@State watchFatherArr:boolean =false
isWatching(){
this.watchFatherArr = true
}
//页面构建
build() {
Scroll(){
Column(){
Text('这是入口组件')
.fontSize(32)
// 子组件
Son({
fatherStr:this.fatherStr,
fatherObjMsg:this.fatherObj.msg,// @prop只能接受简单数据类型 所以要传对象中的属性
fatherArr0:this.fatherArr[0], // @prop只能接受简单数据类型 所以只能传数组中的某一项
})
// 父组件的内容
Button('父组件点击后触发改变:') .margin(12)
.onClick(()=>{
this.fatherStr = '父组件改变后,子组件会更新吗?'
this.fatherObj.msg = '对象中的属性在父组件改变'
this.fatherArr[0] = '从父改了'
})
// 展示父组件绑定的数据
Text(this.fatherStr)
Text(this.fatherObj.msg)
List(){
ForEach(this.fatherArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
}
.margin(12)
List(){
Column(){
Text('watch')
Text('FatherArr改了吗:'+this.watchFatherArr)
}
.backgroundColor(`#ffffff`)
.padding(12)
}
.backgroundColor(Color.Orange)
.padding(12)
}
}}
@Component
struct Son{
@Prop fatherStr:string
// prop 只能接受基本数据类型 string、number、boolean、enum类型,或者 父组件是对象,子组件是对象属性
@Prop fatherObjMsg :string
@Prop fatherArr0:string
build(){
Column(){
Text('这是儿子')
.fontSize(32)
Text('@Prop:从父组件单向同步——'+this.fatherStr)
.fontSize(22)
Column(){
// 对象
Text(this.fatherObjMsg)
.fontSize(22)
}
Column(){
// 数组 prop
Text(this.fatherArr0)
}
}
.backgroundColor(Color.Pink)
.padding(12)
}
}
export type objType ={
id:number;
type:string;
msg:string
}
3. 父子组件双向同步状态和监听状态变化:@Link、@Watch
@Link:子组件需要修改
父组件中的状态 ——双向绑定
注意:@Link装饰器不能在@Entry装饰的自定义组件中使用。(@Preview也不行)
父组件通过 @State修饰符 定义变量,子组件通过 @Link修饰符 获取变量。
@Link是 「双向传递」
,父组件会将变量的「引用」交给子组件,相当于子组件可以直接修改父组件中的变量。
父组件
与Prop不同的是,当子组件使用@Link接收变量时(需要修改变量),父组件传值时需要使用 $。
// 声明变量
@State count:number = 1
// 传值
Son({count:$count})
子组件
// 接收
@Link count:number
@Prop和@Link
@Prop | @Link | |
---|---|---|
同步类型 | 单向同步 | 双向同步 |
允许装饰的变量类型 | string、number、boolean、enum类型 父组件对象类型,子组件是对象属性 不可以是数组、any | 父子类型一致:string、number、boolean、enum、object、class,以及他们的数组 数组中元素发生变化会引起刷新 嵌套类型以及数组中的对象属性无法触发视图更新 |
初始化方式 | 禁止子组件初始化 | 父组件传递,禁止子组件初始化 |
示例
/*
* 页面与子组件之间的参数传递与UI刷新
* * */@Entry
@Component
struct Index {
// 简单数据类型 string,boolean,number 的组件间传递方式
@State linkStr:string = '在父组件初始化'
// 复杂数据类型 对象,数组 的组件间传递方式
// 对象 对象内属性为简单数据类型
@State linkObj:objType ={
id:2,
type:'link',
msg:'对象中的属性发生改变能监听到吗?'
}
// 数组 数组内元素为简单数据类型
@State @Watch('isWatchingLinkArr') linkArr:string[]=['link','父传子','双向']
@State watchLinkArr:boolean=false
isWatchingLinkArr(){
this.watchLinkArr =true
}
build() {
Scroll(){
Column(){
Text('这是入口组件')
.fontSize(32)
// 子组件
Son({
linkStr:$linkStr,// link修饰的变量传递时用 $ 修饰
linkObj:$linkObj,
linkArr:$linkArr
})
// 父组件的内容
Button('父组件点击后触发改变:') .margin(12)
.onClick(()=>{
this.linkStr = 'link,子组件会更新吗?'
this.linkObj.msg = '对象中的属性在父组件改变'
this.linkArr[0] = '从父改了'
})
Text(this.linkStr)
Text(this.linkObj.msg)
List(){
ForEach(this.linkArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
} .margin(12)
Column(){
Text('watch')
Text('LinkArr改了吗:'+this.watchLinkArr)
}
.backgroundColor(`#ffffff`)
.padding(12)
}
.backgroundColor(Color.Orange)
.padding(12)
}
}}
@Component
struct Son{
@Link linkStr:string //==@Link装饰器不能在@Entry装饰的自定义组件中使用。(@Preview也不行)==
@Link linkObj:objType
@Link linkArr:string[]
build(){
Column(){
Text('这是儿子')
.fontSize(32)
// 儿子的儿子 孙子 Consume接受时不用传值
Grandson()
Button('从子组件点击后改变')
.onClick(()=>{
// link 接受的参数可在子组件中改变
this.linkStr = '子组件改变后更新吗?'
this.linkObj.msg ='在子组件中改变对象中的属性'
this.linkArr=['从子改了']
}).margin(12)
Text('@Link:从父组件双向同步——'+this.linkStr)
.fontSize(22)
Column(){
// 对象
Row(){
Text(this.linkObj.type)
.fontSize(24)
Text(this.linkObj.msg)
.fontSize(22)
}
}
Column(){
// 数组 link
List(){
ForEach(this.linkArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
} .margin(12)
}
}
.backgroundColor(Color.Pink)
.padding(12)
}
}
export type objType ={
id:number;
type:string;
msg:string
}
@Watch:监听状态变化
注意:
- @Watch 不能单独使用,只能监听由 @Link 、@State 、@Prop等装饰器修饰的变量
- 在第一次初始化的时候,@Watch装饰的方法不会被调用,即认为初始化不是状态变量的改变。只有在后续状态改变时,才会调用@Watch回调方法。
- @Watch在ArkUI框架内部判断数值有无更新使用的是严格相等( === ),遵循严格相等规范。
限制条件: - 为了避免循环的产生,建议不要在@Watch的回调方法里修改当前装饰的状态变量
- 回调函数应仅执行快速运算
- 不建议在@Watch函数中调用async await 等异步行为
@Component
struct TotalView {
@Prop @Watch('onCountUpdated') count: number;
@State total: number = 0;
// @Watch 回调
onCountUpdated(propName: string): void {
this.total += this.count;
}
build() {
Text(`Total: ${this.total}`)
}
}
@Entry
@Component
struct CountModifier {
@State count: number = 0;
build() {
Column() {
Button('add to basket')
.onClick(() => {
this.count++
})
TotalView({ count: this.count })
}
}
}
4.跨组件通信:@Provide、@Consume
父组件通过 @Provide修饰符 定义变量,子组件通过 @Consume修饰符 获取变量。
不同于@State,父组件「不需要传递参数」,子组件通过 @Consume修饰符 「直接使用变量」即可。
允许装饰的数据类型
Object、class、string、number、boolean、enum类型,以及这些类型的数组
不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。
必须指定类型。@Provide变量的@Consume变量的类型必须相同。
观察变化
- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
- 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。
- 当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。
父组件
// 声明状态
@Provide count:number = 1
// 无需传值
Son()
后代组件
// 接收
@Consume count:number
示例
@Entry
@Component
struct Index {
// 简单数据类型 string,boolean,number 的组件间传递方式
// 跨组件通信
@Provide provideStr:string ='需要在最顶层组件初始化'
// 复杂数据类型 对象,数组 的组件间传递方式
// 对象 对象内属性为简单数据类型
@Provide provideObj:objType={
id:3,
type:'Provide',
msg:'对象中的属性发生改变能监听到吗?'
}
// 数组 数组内元素为简单数据类型
@Provide @Watch('isWatchingProvideArr') provideArr:string[]=['provide','爷传孙','双向']
@State watchProvideArr:boolean=false
isWatchingProvideArr(){
this.watchProvideArr =true
}
build() {
Scroll(){
Column(){
Text('这是入口组件')
.fontSize(32)
// 跳转到objectLink相关页面
Button('跳转到objectLink相关页面')
.onClick(()=>{
router.pushUrl({
url:"pages/objectLink",
params: this.paramsInfo
})
}).margin(12)
// 子组件
Son()
// 父组件的内容
Button('父组件点击后触发改变:') .margin(12)
.onClick(()=>{
this.provideStr = '从爷爷这改了'
this.provideObj.msg='对象中的属性在provide组件改变'
this.provideArr[0] = '从爷爷这改了'
})
Text(this.provideStr)
Text(this.provideObj.msg)
List(){
ForEach(this.provideArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
} .margin(12)
Column(){
Text('watch')
Text('ProvideArr改了吗:'+this.watchProvideArr)
}
.backgroundColor(`#ffffff`)
.padding(12)
}
.backgroundColor(Color.Orange)
.padding(12)
}
}}
@Component
struct Son{
build(){
Column(){
Text('这是儿子')
.fontSize(32)
// 儿子的儿子 孙子 Consume接受时不用传值
Grandson()
}
.backgroundColor(Color.Pink)
.padding(12)
}
}
@Component
struct Grandson{
@Consume provideStr:string
@Consume provideObj:objType
@Consume provideArr:string[]
build(){
Column(){
Text('这是孙子').fontSize(32)
Column(){
Button('从后代组件点击后改变')
.onClick(()=>{
this.provideStr = '从孙子这改了'
this.provideObj.msg = '从孙子这改了'
this.provideArr[0] = '从孙子这改了'
}).margin(12)
Text('Consume从爷爷那里直接拿到的:'+this.provideStr)
.fontSize(22)
Text(this.provideObj.type+':'+this.provideObj.msg)
.fontSize(22)
List(){
ForEach(this.provideArr,(item:string)=>{
ListItem(){
Text(item)
}
},item=> item)
} .margin(12)
}
}
.backgroundColor('#65a739')
.padding(12)
}
}
export type objType ={
id:number;
type:string;
msg:string
}