旧时代的船?来看看鸿蒙V1状态管理装饰器

前言:

在我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面,就需要引入"状态"的概念。

ArkUI 状态管理V1提供了多种装饰器,通过使用这些装饰器,状态变量不仅可以观察在组件内的改变,还可以在不同组件层级间传递,比如父子组件、跨组件层级,也可以观察全局范围内的变化。

从数据的传递形式和同步类型层面看,装饰器也可分为:

  • 只读的单向传递;
  • 可变更的双向传递。

V1的装饰器

@State

在所有的状态管理装饰器中@State是最基础的一个,他能够赋予监听一个属性的数据状态的变化。

@State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就可以触发其直接绑定UI 组件的刷新。当状态改变时,UI会发生对应的渲染改变。

js 复制代码
interface iHobby{
  number:number;
  swim:boolean;
  run:boolean;
}

interface iPerson{
  name:string;
  age:number;
  hobby:iHobby;
}
@Entry
@Component
struct Index {
  @State num:number=0
  @State arr:number[]=[1,2,3]
  @State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
  build() {
    Column() {
      Text('num:'+this.num)
      Text('arr:'+this.arr)
      Text('p1:'+JSON.stringify(this.p1))
      Button('++')
        .onClick(() => {
          this.num++
          this.arr.push(this.num)
          this.p1.age++
          // this.p1.hobby.number++
        })
    }
    .width('100%')
  }
}

但值得注意的是对于复杂数据类型,@State并不能监听到深层的数据变化,只能监听浅层也就是第一层的数据变化。

js 复制代码
interface iHobby{
  number:number;
  swim:boolean;
  run:boolean;
}

interface iPerson{
  name:string;
  age:number;
  hobby:iHobby;
}
@Entry
@Component
struct Index {
  @State num:number=0
  @State arr:number[]=[1,2,3]
  @State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
  build() {
    Column() {
      // Text('num:'+this.num)
      // Text('arr:'+this.arr)
      Text('p1:'+JSON.stringify(this.p1))
      Button('++')
        .onClick(() => {
          // this.num++
          // this.arr.push(this.num)
          // this.p1.age++
          this.p1.hobby.number++
        })
    }
    .width('100%')
  }
}

点击增加按钮视图没有发生任何的改变

@Prop

@Prop一般在子组件中使用,它能够接受来自于父组件传递的数据,并且能够同步父组件的响应式变化。

但值得注意的是,它本身不具备修改父组件数据的能力,如果强行修改则只会修改子组件中的数据不会影响父组件的数据。

js 复制代码
interface iHobby{
  number:number;
  swim:boolean;
  run:boolean;
}

interface iPerson{
  name:string;
  age:number;
  hobby:iHobby;
}
@Entry
@Component
struct Index {
  @State num:number=0
  @State arr:number[]=[1,2,3]
  @State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
  build() {
    Column() {
      Text('父组件num:'+this.num)
      Text('父组件arr:'+this.arr)
      Text('父组件name:'+JSON.stringify(this.p1))
      Button('父组件++')
        .onClick(() => {
          this.num++
          this.arr.push(this.num)
          this.p1.age++
          // this.p1.hobby.number++
        })
      Divider()
      Son({
        num:this.num,
        arr:this.arr,
        p1:this.p1
      })
    }
    .width('100%')
  }
}

@Component
struct Son {
  @Prop num:number
  @Prop arr:number[]
  @Prop p1:iPerson
  build() {
    Column(){
      Text('子组件num:'+this.num)
      Text('子组件arr:'+this.arr)
      Text('子组件name:'+JSON.stringify(this.p1))
      Button('子组件++')
        .onClick(()=>{
        this.num++
        this.arr.push(this.num)
        this.p1.age++
        // this.p1.hobby.number++
      })
    }
  }
}

@Link弥补了前文当中提到的@Prop,子组件的修改不会引起父组件的数据变化,实现了父子组件的双向同步。

js 复制代码
interface iHobby{
  number:number;
  swim:boolean;
  run:boolean;
}

interface iPerson{
  name:string;
  age:number;
  hobby:iHobby;
}
@Entry
@Component
struct Index {
  @State num:number=0
  @State arr:number[]=[1,2,3]
  @State p1:iPerson={name:'张三',age:18,hobby:{number:0,swim:true,run:false}}
  build() {
    Column() {
      Text('父组件num:'+this.num)
      Text('父组件arr:'+this.arr)
      Text('父组件name:'+JSON.stringify(this.p1))
      Button('父组件++')
        .onClick(() => {
          this.num++
          this.arr.push(this.num)
          this.p1.age++
          // this.p1.hobby.number++
        })
      Divider()
      Son({
        num:this.num,
        arr:this.arr,
        p1:this.p1
      })
    }
    .width('100%')
  }
}

@Component
struct Son {
  @Link num:number
  @Link arr:number[]
  @Link p1:iPerson
  build() {
    Column(){
      Text('子组件num:'+this.num)
      Text('子组件arr:'+this.arr)
      Text('子组件name:'+JSON.stringify(this.p1))
      Button('子组件++')
        .onClick(()=>{
          this.num++
          this.arr.push(this.num)
          this.p1.age++
          // this.p1.hobby.number++
        })
    }
  }
}

@Provide & @Consume

@Provide@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。

js 复制代码
@Entry
@Component
struct Index {
  @Provide num:number=0
  @Provide arr:number[]=[1,2,3]
  build() {
    Column() {
      Text('父组件num:'+this.num)
      Text('父组件arr:'+this.arr)
      Button('父组件++')
        .onClick(() => {
          this.num++
          this.arr.push(this.num)
        })
      Divider()
      Son()
    }
    .width('100%')
  }
}
@Component
struct Grandson {
  @Consume num:number
  @Consume arr:number[]
  @Consume p1:iPerson
  build() {
    Column(){
      Text('孙组件num:'+this.num)
      Text('孙组件arr:'+this.arr)
      Button('孙组件++')
        .onClick(()=>{
          this.num++
          this.arr.push(this.num)
        })
    }
  }
}

@Component
struct Son {
  @Consume num:number
  @Consume arr:number[]
  @Consume p1:iPerson
  build() {
    Column(){
      Text('子组件num:'+this.num)
      Text('子组件arr:'+this.arr)
      Button('子组件++')
        .onClick(()=>{
          this.num++
          this.arr.push(this.num)
        })
      Divider()
      Grandson()
    }
  }
}

上文所述的装饰器,仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。

js 复制代码
//【固定】1. @Observed 装饰类
@Observed
class ClassA{
  // 略
}

//2. 父组件
@Component 
struct ParentCom{
  // 【非固定写法】根据需求来定义数据
  @State 变量名: ClassA[] = [ new ClassA(),new ClassA(), ]
  @State 变量名:ClassA = new ClassA()
  @State 变量名: ClassA[][] = [[new ClassA(),new ClassA()],[new ClassA(),new ClassA()] ]
}

// 【固定】3. 子组件
@Component 
struct ChildCom{
  @ObjectLink 变量名: ClassA
}

需要对class声明的类名使用@Observed,对于传递的子组件使用@ObjectLink

js 复制代码
@Observed
class iPerson {
  name:string
  age:number
  hobby:iHobby
  constructor(name:string, age:number,hobby:iHobby) {
    this.name=name
    this.age=age
    this.hobby=hobby
  }
}
class iHobby{
  num:number
  constructor(num:number) {
    this.num=num
  }
}

@Entry
@Component
struct Index {
  @State p1:iPerson=new iPerson('张三',18,new iHobby(1))
  build() {
    Column() {
      Text('name:'+this.p1.name)
      Text('age:'+this.p1.age)
      Text('hobby:'+this.p1.hobby.num)
      Button('父组件++')
        .onClick(() => {
          this.p1.age++
          this.p1.hobby.num++
        })
      Divider()
      Son({
        p1:this.p1,
      })
    }
    .width('100%')
  }
}

@Component
struct Son {
  @ObjectLink p1:iPerson
  build() {
    Column(){
      Text('name:'+this.p1.name)
      Text('age:'+this.p1.age)
      Text('hobby:'+this.p1.hobby.num)
      Button('子组件++')
        .onClick(()=>{
          this.p1.age++
          this.p1.hobby.num++
        })
    }
  }
}
相关推荐
sorryhc13 分钟前
钉钉红包性能优化之路
前端·javascript·性能优化
袁煦丞33 分钟前
你的在线相册管理专家Piwigo:cpolar内网穿透实验室第487个成功挑战
前端·程序员·远程工作
꧁༺摩༒西༻꧂39 分钟前
Python生成日历导出Excel
java·前端·python
lqj_本人43 分钟前
鸿蒙OS&UniApp页面切换动效实战:打造流畅精致的转场体验#三方框架 #Uniapp
华为·uni-app·harmonyos
蒟蒻小袁1 小时前
力扣面试150题--二叉树的层平均值
算法·leetcode·面试
Mintopia2 小时前
计算机图形学的奇幻之旅:第三天探索
前端·javascript·计算机图形学
Mintopia2 小时前
Three.js 物理材质:打造 3D 世界的 “魔法皮肤”
前端·javascript·three.js
知识分享小能手2 小时前
Typescript学习教程,从入门到精通,TypeScript 泛型与类型操作详解(二)(17)
前端·javascript·学习·typescript·jquery·前端网页学习
stark张宇3 小时前
Web - Javascript 函数与DOM、BOM
前端
上海张律师3 小时前
组件截图sdk -- screenshot_hm介绍 ##三方SDK##
前端·harmonyos