@State
状态变量
1、在当前页面使用
2、@State修饰的成员变量改变,会让UI跟着一起刷新更改。
3、@State修饰的如果是class或Object时,只能监听到第一层属性的更改。
示例1
用@State修饰一个string类型的成员变量,点击按钮时修改它的值,发现UI也发生变化
@State message: string = "hello"
Column() {
Text(this.message)
Button("按钮").width(100).height(40).onClick(() => {
this.message = "你好"
})
}
示例2
用@State修饰一个Object类型的成员变量
1.点击按钮1时修改它的整体值,发现UI也发生变化
2.点击按钮2时修改它的第一层值,发现UI发生变化
3.点击按钮3时修改它的第二层值,发现UI未发生变化,其实值是有改变的
4.点击按钮4时修改它的第一层对象整个值,发现UI发生变化
// 声明对象类型
interface Animal {
color: string
weight: number
}
interface Man {
name: string
age: number
dog: Animal
}
// @State定义成员变量
@State man: Man = {
name: "jonas",
age: 31,
dog: {
color: "white",
weight: 30
}
}
// 界面使用
Text(`man is ${this.man.name}, his dog color is ${this.man.dog.color}`)
Button("按钮1").width(100).height(40).onClick(() => {
this.man = {
name: "BOb",
age: 31,
dog: {
color: "black",
weight: 30
}
}
})
Button("按钮2").width(100).height(40).onClick(() => {
this.man.name = "Joe"
})
Button("按钮3").width(100).height(40).onClick(() => {
this.man.dog.color = "red"
})
Button("按钮4").width(100).height(40).onClick(() => {
this.man.dog = {
color : "yellow",
weight: 30
}
})
@Prop 单向传递
父子组件单向传递
1、@Prop装饰的变量可以和父组件建立单向的同步关系
2、@Prop装饰的变量是可以变的,但是变化不会同步回其父组件
示例1
下面例子中,父组件声明了一个状态变量car 子组件声明了一个单向传递变量car来接收父组件的car。
默认展示的就是父子组件开的车都是宝马,当点击父组件的换车按钮,发现父子组件都变成了开奔驰。如果是点击子组件换车按钮,发现只有子组件变为了开奔驰,父组件没变。
@Component
struct FatherComponent {
@State car: string = "宝马"
build() {
Column() {
Text("父组件")
Text(`我开着${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
SonComponent({car: this.car})
}
}
}
@Component
struct SonComponent {
@Prop car: string
build() {
Column() {
Text("子组件")
Text(`开着爸爸的${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
}
}
}
示例2
如果希望子组件的状态修改后能够影响父组件的状态,就需要使用回调函数来间接修改
下面例子中子组件增加了一个函数类型changeCar属性,在父组件中传入一个函数,回调里中返回一个string值,将这个值赋值给自己的car状态属性。当点击子组件的换车按钮时,调用changeCar并传入新的值"奔驰",此时父组件的car值变为"奔驰",连带着子组件也会跟着变化。
@Component
struct FatherComponent {
@State car: string = "宝马"
build() {
Column() {
Text("父组件")
Text(`我开着${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
// 这里必须用箭头函数,否则this.car找到的就是子组件里的car了
SonComponent({car: this.car, changeCar: (car: string) => {
this.car = car;
}})
}
}
}
@Component
struct SonComponent {
@Prop car: string
changeCar = (car:string) => {}
build() {
Column() {
Text("子组件")
Text(`开着爸爸的${this.car}`)
Button("换车").onClick(()=>{
this.changeCar("奔驰")
})
}
}
}
@Link 双向同步
可以实现父组件和子组件的双向同步
1、父组件定义一个状态属性
2、子组件用@Link修饰同名属性
3、父组件将状态属性传递给子组件
示例
还是@Prop的例子,我们将@Prop改为@Link就可以实现修改子组件的属性也让父组件的属性跟着变化了
@Component
struct FatherComponent {
@State car: string = "宝马"
build() {
Column() {
Text("父组件")
Text(`我开着${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
SonComponent({car: this.car})
}
}
}
@Component
struct SonComponent {
@Link car: string
build() {
Column() {
Text("子组件")
Text(`开着爸爸的${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
}
}
}
@Provide + @Consume 跨层级双向同步
创建三层UI,第一层为爷爷层,第二层父亲层,第三层儿子层,三层嵌套,爷爷层在最底部。
爷爷层如果想让自己的状态属性跨越父亲层与孙子层状态属性同步,按照之前的几种方法必须要一层层先传递给父亲层,再让父亲层传递给孙子层,这样是非常繁琐的。
这时候使用@Provide + @Consume就可以跨多层级直接同步
爷爷层用@Provide声明一个状态属性 ,孙子层用@Consume声明一个同名的状态属性来接收,就可以直接同步了。
示例
下面的例子点击爷爷层的换车按钮,爷爷层和孙子层的UI都会更改
点击孙子层的换车按钮,爷爷层和孙子层的UI也都会更改
@Component
struct GrandFatherComponent {
@Provide car: string = "宝马"
build() {
Column() {
Text("爷爷组件")
Text(`我开着${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
FatherComponent()
}
}
}
@Component
struct FatherComponent {
build() {
Column() {
Text("父组件")
SonComponent()
}
}
}
@Component
struct SonComponent {
@Consume car: string
build() {
Column() {
Text("子组件")
Text(`开着爷爷的${this.car}`)
Button("换车").onClick(()=>{
this.car = "奔驰"
})
}
}
}
@Observed + @ObjectLink 监听深层属性双向同步
说明:上面学的@Link修饰器修饰的状态属性只能监听到第一层的值变动,如果想监听更深层次的状态属性就要使用@Observed + @ObjectLink
作用:用于在涉及嵌套对象或数组的场景中进行双向数据同步
注意:@ObjectLink修饰符不能用在Entry修饰的组件中,@Observed 只能修饰class不能修饰interface
示例1
下面例子@Observed修饰Man类,循环创建多个自定义组件,并将数组中的对象传入。在自定义组件中用@ ObjectLink接收,在自定义组件内改变对象值,父组件中会被同步更改并刷新UI。
@Observed
class Man {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
@Component
struct Cell {
@ObjectLink man: Man
build() {
Column() {
Text(`我的名字叫${this.man.name}`)
Button("按钮").onClick(()=>{
this.man.name = "bob"
})
}
}
}
@Component
export struct HomePage {
@State list: Man[] = [new Man("jonas", 31), new Man("mary", 29)]
build() {
Column() {
List({space: 10}) {
ForEach(this.list, (item: Man, index: number) => {
Cell({man: item})
})
}
}
}
}
示例2
定义Man对象它有个对象属性Son, Son类用@ Observed修饰。在父组件中,将man对象的son属性传递给子组件,子组件用@ObjectLink接收。子组件中修改son的name属性,可以同步修改父组件的man属性的son值,不过并不能触发父组件UI刷新。
如果子组件用@Link接收会闪退,用@Prop接收,并不同步修改父组件的值。
@Observed
class Son {
name: string
constructor(name: string) {
this.name = name
}
}
class Man {
name: string
son: Son
constructor(name: string, son: Son) {
this.name = name;
this.son = son;
}
}
@Component
struct Item {
@ObjectLink son: Son
build() {
Column() {
Text(`子视图中儿子的名字是${this.son.name}`)
Button("按钮").onClick(()=>{
this.son.name = "BOB"
})
}
}
}
@Component
export struct HomePage {
@State list: Man[] = [new Man("jonas", 31), new Man("mary", 29)]
build() {
Column() {
Text(`父视图中我的儿子的名字是${this.man.son.name}`)
Item({son: this.man.son})
}
}
}