旧时代的船?来看看鸿蒙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++
        })
    }
  }
}
相关推荐
森蓝情丶5 小时前
我给 AI 搭了个法庭:一个前端仔的 LangGraph 实战全记录
前端·后端
爱勇宝5 小时前
干了近 8 年,一夜之间被裁:AI 时代,程序员最该害怕的不是 AI
前端·后端·程序员
Pedantic6 小时前
Combine 框架学习笔记
前端
runnerdancer6 小时前
Agent如何加载执行Skill的脚本
前端·agent
yingyima6 小时前
VS Code 正则替换技巧:从凌晨3点的服务器报警开始
前端
默_笙6 小时前
🛬 我让 AI 帮我写了一个打飞机游戏,结果 Canvas 把我整不会了
前端·javascript
梯度不陡6 小时前
AI 到底能不能从零写软件?ProgramBench 和 RepoZero 给出了两种答案
前端·javascript·面试
冬奇Lab7 小时前
每日一个开源项目(第137篇):Penpot - 真正开源的设计协作工具,SVG 原生格式消灭设计-开发鸿沟
前端·开源·设计
nuIl7 小时前
实现一个 Coding Agent(7):Skills
前端·agent·cursor
nuIl7 小时前
实现一个 Coding Agent(8):会话持久化与多会话
前端·agent·cursor