旧时代的船?来看看鸿蒙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++
        })
    }
  }
}
相关推荐
红辣椒...4 分钟前
codex+第三方模型
java·服务器·前端
木子雨廷6 分钟前
Flutter 使用 flutter_flavorizr 多渠道打包
前端·flutter
大雷神6 分钟前
第28篇|相机失败态:没有权限、没有设备、会话失败时如何提示
harmonyos
环境工程笔记8 分钟前
浏览器自动化跑成功了,为什么结果还是不对?
前端
东风破_10 分钟前
一文搞懂 JavaScript 变量声明:var、let、const 到底有什么区别?
前端·javascript
问心无愧051313 分钟前
ctf show web入门261
android·前端·笔记
触底反弹15 分钟前
你真的理解 JavaScript 变量提升(Hoisting)吗?从 V8 引擎编译原理深入剖析
前端·面试
蜡台27 分钟前
Vue2 使用 typescript 教程
前端·vue.js·typescript
光影少年40 分钟前
Redux Toolkit 用法、解决原生Redux 冗余问题
开发语言·前端·javascript·react.js·中间件·前端框架·ecmascript
不羁的木木1 小时前
Form Kit(卡片开发服务)学习笔记05-进阶实战与性能优化
笔记·学习·harmonyos