ArkUI--状态管理@Prop@Link@Provide@Consume

79 阅读1分钟

1.@Prop和@Link

当父子组件之间需要数据同步时,可以使用@Prop和@Link装饰器:

(1)@Prop

@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。

@Entry
@Component
struct  Index {

  @State grade:number = 59

  build() {
    Column() {
      GradeShow({grade:this.grade})
      EvaluateShow({grade:this.grade})
      Button('父布局改变')
        .onClick(()=>{
          this.grade++
        })
    }.width('100%')
  }
}

@Component
struct GradeShow {
  @Prop grade:number

  build() {
    Column() {
      Text(`当前的分数:${this.grade}`)
      Button('独自改变')
        .onClick(()=>{
          this.grade++
        })
    }

  }

}

@Component
struct EvaluateShow {
  @Prop grade:number

  build() {
    if(this.grade<60) {
      Text('您不及格哦')
    } else if(this.grade<70) {
      Text('你及格了')
    } else if(this.grade<80) {
      Text('一般')
    } else if(this.grade<90) {
      Text('良好')
    } else {
      Text('优秀')
    }
  }
}

Prop.png

效果如上,如果点击“独立改变”呢,只会改变第一个子View的显示,并不能改变父组件内grade的值;如果点击的是“父布局改变”,grade发生变化,子组件发生变化。因为,@Prop是父向子建立单向的关系。

(2)@Link

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

class Hobby {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

interface GirlFriend {
  name:string,
  age:number
}

@Component
struct HobbyChild {

  @Prop hobby:Hobby
  @Prop gf:GirlFriend

  build() {
    Text(`兴趣:${this.hobby.name},女友:${this.gf.name},年纪:${this.gf.age}`)
  }
}

@Entry
@Component
struct Index {
  @State all: number = 0
  @State current: number = 0
  @State hobby:Hobby = new Hobby('Ball')
  @State gf:GirlFriend = {
    name:'Rose',
    age:18
  }

  build() {
    Column() {
      TaskProcess({ all: this.all, current: this.current })
      TaskCard({ all: $all, current: $current })
      HobbyChild({hobby:this.hobby,gf:this.gf})
      Button('改变爱好')
        .onClick(()=>{
          this.hobby.name = 'book'
          this.gf.age = 20
        })
    }.width('100%')
  }
}

interface Task {
  name: string,
  chosen: boolean
}

@Component
struct TaskProcess {
  @Prop current: number
  @Prop all: number

  build() {
    Text(`${this.current}/${this.all}`)
  }
}


@Component
struct TaskCard {
  @Link current: number
  @Link all: number
  @State taskList: Array<Task> = []

  build() {
    Column() {
      Button('添加任务')
        .onClick(() => {
          this.taskList.push({
            name: `第${this.taskList.length}个任务`,
            chosen: false
          })
          this.current = this.taskList.filter((item) => item.chosen).length
          this.all = this.taskList.length
        })
      ForEach(this.taskList, (item: Task) => {
        Row() {
          Text(item.name)
          Checkbox()
            .select(item.chosen)
            .onChange((value) => {
              item.chosen = value
              this.current = this.taskList.filter((item) => item.chosen).length
            })
        }.justifyContent(FlexAlign.SpaceBetween)
        .width('100%')

        .padding({
          left: 20,
          right: 20
        })
      })
    }.width('100%')

  }
}

2.@Prophe和和@Link支持的数据类型

(1)@Prop

支持Object、class、string、number、boolean、enum类型,以及这些类型的数组。

不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。

支持Date类型。

不允许子组件初始化。

(2)@Link

支持Object、class、string、number、boolean、enum类型,以及这些类型的数组。

支持Date类型。

类型必须被指定,且和双向绑定状态变量的类型相同。

不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。

父组件传递,禁止子组件初始化。

3.@Providehe和@Consume

可以跨组件提供类似于@State和@Link的双向同步。我们改造一下上述的例子:

class Hobby {
  name: string

  constructor(name: string) {
    this.name = name
  }
}

interface GirlFriend {
  name:string,
  age:number
}

@Component
struct HobbyChild {

  @Prop hobby:Hobby
  @Prop gf:GirlFriend

  build() {
    Text(`兴趣:${this.hobby.name},女友:${this.gf.name},年纪:${this.gf.age}`)
  }
}

class TaskInfo {
  all:number = 0
  current:number = 0
}

@Entry
@Component
struct Index {
  @Provide taskInfo:TaskInfo = new TaskInfo()
  @State hobby:Hobby = new Hobby('Ball')
  @State gf:GirlFriend = {
    name:'Rose',
    age:18
  }

  build() {
    Column() {
      TaskProcess()
      TaskCard()
      HobbyChild({hobby:this.hobby,gf:this.gf})
      Button('改变爱好')
        .onClick(()=>{
          this.hobby.name = 'book'
          this.gf.age = 20
        })
    }.width('100%')
  }
}

interface Task {
  name: string,
  chosen: boolean
}

@Component
struct TaskProcess {
  @Consume taskInfo:TaskInfo

  build() {
    Text(`${this.taskInfo.current}/${this.taskInfo.all}`)
  }
}


@Component
struct TaskCard {
  @Consume taskInfo:TaskInfo
  @State taskList: Array<Task> = []

  build() {
    Column() {
      Button('添加任务')
        .onClick(() => {
          this.taskList.push({
            name: `第${this.taskList.length}个任务`,
            chosen: false
          })
          this.taskInfo.current = this.taskList.filter((item) => item.chosen).length
          this.taskInfo.all = this.taskList.length
        })
      ForEach(this.taskList, (item: Task) => {
        Row() {
          Text(item.name)
          Checkbox()
            .select(item.chosen)
            .onChange((value) => {
              item.chosen = value
              this.taskInfo.current = this.taskList.filter((item) => item.chosen).length
            })
        }.justifyContent(FlexAlign.SpaceBetween)
        .width('100%')

        .padding({
          left: 20,
          right: 20
        })
      })
    }.width('100%')

  }
}

image.png

@Provide和@Consume能跨组件使用,不用显示传参,必然会带来一些资源上的损耗。在不用跨组件通信的场景下,我们还是尽量使用@Prop和@Link。