ArkTS 状态管理@Prop、@Link

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

实现的案例之中,代码时平铺直叙的,阅读性可理解性比较差。我们应改遵循组件化开发的思想。

在我们使用组件开发的时候,遇到数据同步问题的时候,@State状态是解决不了的。所以就要用到@Prop和@Link

我们可以将任务进度卡片封装为一个组件、将任务按钮和卡片列表封装为另外一个组件,来实现组件化的目的。 

任务进度卡片封装

我们先用@State尝试着封装任务进度卡片组件,然后再入口函数中使用。

@Component
struct TaskStatistics {
  @State finishTask: number = 0
  @State totalTask: number = 0
  build(){
    Row(){
      Text("任务进度")
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Stack(){
        Progress({
          value:this.finishTask,
          total:this.totalTask,
          type:ProgressType.Ring
        })
          .width(100)
        Row(){
          Text(this.finishTask.toString())
            .fontSize(24)
            .fontColor('#36d')
          Text(' / ' +this.totalTask.toString())
            .fontSize(24)
        }
      }
    }
    .card()
    .margin({top:20,bottom:10})
    .justifyContent(FlexAlign.SpaceEvenly)
  }
}

这样一来  PropPage组件就是父组件、TaskStatistics就是子组件。

其中的任务数量是由PropPage来维护的。所以我们需要从父亲向子传递数量属性,这时候我们会发现报错了,提示@State不支持之类的。

 这说明再父子传值的时候,@State状态已经不支持了。我们这个时候可以使用Prop和Link两个装时期。

Prop和Link装饰器

@Prop @Link
同步类型 单向同步 双向同步
允许装饰的变量类型

1.@Prop只支持string、number、boolean、enum类型

2.父组件对象类型、子组件对象属性

3.不可以是数组、any

1.父子类型一致:string、number、boolean、enum、object、class,以及他们的数组

2.数组中元素增、删、替换会引起刷新

3.嵌套类型以及数组中的对象属性无法触发视图更新。

初始化方式 不允许子组件初始化 父组件传递,禁止子组件初始化

@Prop是单向同步,父组件的改变会立即传递给子组件,但是子组件的修改不会影响父组件,他传递的是拷贝的值。

@LInk是双向同步,传递的是变量的引用,父亲和儿子使用的是一个变量,所以两方都会感知到变量的改变。

任务列表的封装

在封装任务列表的时候,要注意一些问题,比如说build函数下不能放入多个顶层,所以我们加一个Column容器,我们将按钮函数也加入。因为任务列表同任务进度是同步更新数据的,这里我们使用Link状态来完成。

@Component
struct TaskList {
  // 任务数量
  @State tasks: Task[] = []
  // 完成任务列表
  @Link finishTask: number
  // 所有的任务列表
  @Link totalTask: number
  // 通过过滤方法 更新已完成的任务数量
  handleTaskChange(){
    // 更新任务总数量
    this.totalTask = this.tasks.length
    // 已经完成的任务数量
    this.finishTask = this.tasks.filter(item => item.finished).length
  }
  build(){
      Column(){
        // 2.新增任务按钮
        Button('新增任务')
          .width(200)
          .onClick(()=>{
            // 1.新增任务
            this.tasks.push(new Task())
            // 2.更新任务数组
            this.totalTask = this.tasks.length
          })
        // 3.卡片列表
        List({space:10}){
          ForEach(
            this.tasks,
            (item: Task,index)=>{
              ListItem(){
                Row(){
                  Text(item.name)
                    .fontSize(20)
                  Checkbox()
                    .select(item.finished)
                    .onChange(val=>{
                      item.finished = val
                      // 通过过滤方法 更新已完成的任务数量
                      this.handleTaskChange()
                    })
                }
                .card()
                .justifyContent(FlexAlign.SpaceBetween)
              }
              .swipeAction({end:this.DeleteButton(index)})
            }
          )
        }
        .width('100%')
        .layoutWeight(1)
        .alignListItem(ListItemAlign.Center)
      }
  }
  @Builder DeleteButton(index: number){
    Button("删除")
      .onClick(()=>{
        this.tasks.splice(index,1)
        this.handleTaskChange()
      })
  }

  }

我们在使用Link的时候,父类里面穿参数,需要使用$语法。 

我们会发现效果与我们最开始的效果是一样一样的。

验证数据类型

我们来验证一下@Prop和@Link的数据类型

我们来验证是否支持对象属性

1.创建一个统计信息的类 

// 任务统计信息
class StaticInfo{
  totalTask: number = 0
  finishTask: number = 0
}

父组件中直接实例化 传值直接传入属性

2.子组件使用对象属性

我们在组件中使用对象属性来完成

struct TaskList {
  // 任务数量
  @State tasks: Task[] = []
  @Link stat: StaticInfo
  // 通过过滤方法 更新已完成的任务数量
  handleTaskChange(){
    // 更新任务总数量
    this.stat.totalTask = this.tasks.length
    // 已经完成的任务数量
    this.stat.finishTask = this.tasks.filter(item => item.finished).length
  }

 

 @Provide和@Consume装饰器

@Provide和@Consume可以跨组件提供类似@State和@Link的双向同步功能。

@Provide和@Consume内部就能传递信息,不需要手写传递。 

如何使用

我们可以直接使用provide和consume,参数就不需要传递了,内部帮忙维护,代价是资源的损耗。所以我们在使用的时候优先考虑@State和@Link状态,遇到跨组件的时候,在使用@Provide和@Consume。

 

猜你喜欢

转载自blog.csdn.net/a_strong_pig/article/details/135062448