旧时代的船?来看看鸿蒙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++
        })
    }
  }
}
相关推荐
搞瓶可乐19 分钟前
鸿蒙ArkTs实战之截图保存图片到相册,详细教程,不使用SaveButton的方法,附上源码和效果图
华为·harmonyos·arkts·保存图片·操作沙箱·鸿蒙解决方案·媒体操作
年纪轻轻只想躺平21 分钟前
JavaScript ES6+ 最佳实践
前端·javascript·es6
Edward-tan22 分钟前
【玩转全栈】—— 无敌前端究极动态组件库--Inspira UI
前端·spira-ui
qq_2780637142 分钟前
vue2 结合后端预览pdf 跨域的话就得需要后端来返回 然后前端呈现
前端·pdf
桥豆麻袋939344 分钟前
vite 初始化react项目
前端·react.js·前端框架
2401_837088501 小时前
CSS相对定位与绝对定位
前端·css
__Benco1 小时前
OpenHarmony平台驱动开发(九),MIPI DSI
人工智能·驱动开发·harmonyos
梦想平凡1 小时前
开元类双端互动组件部署实战全流程教程(第1部分:环境与搭建)
运维·服务器·前端·游戏·node.js
HelloRevit2 小时前
React -> AI组件 -> 调用Ollama模型, qwen3:1.7B非常聪明
前端·react.js·前端框架
geovindu2 小时前
javascript: Multi-page PDF in Canvas using PDFJS 5.1
前端·javascript