鸿蒙应用开发——ArkTS 组件状态管理(一)@State、@Prop、@Link、@Provide和@Consume、@Watch的使用详细教程含示例代码

场景装饰器是否需要本地初始化数据类型
组件内的状态管理@Stateany,监听不到数组和对象嵌套复杂数据类型内的变化
从父组件单向同步状态@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:监听状态变化

注意:

  1. @Watch 不能单独使用,只能监听由 @Link 、@State 、@Prop等装饰器修饰的变量
  2. 在第一次初始化的时候,@Watch装饰的方法不会被调用,即认为初始化不是状态变量的改变。只有在后续状态改变时,才会调用@Watch回调方法。
  3. @Watch在ArkUI框架内部判断数值有无更新使用的是严格相等( === ),遵循严格相等规范。
    限制条件:
  4. 为了避免循环的产生,建议不要在@Watch的回调方法里修改当前装饰的状态变量
  5. 回调函数应仅执行快速运算
  6. 不建议在@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  
}

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值