旧时代的船?来看看鸿蒙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++
        })
    }
  }
}
相关推荐
四月_h11 分钟前
vue2动态实现多Y轴echarts图表,及节点点击事件
前端·javascript·vue.js·echarts
文心快码BaiduComate34 分钟前
用Zulu轻松搭建国庆旅行4行诗网站
前端·javascript·后端
行者..................2 小时前
手动编译 OpenCV 4.1.0 源码,生成 ARM64 动态库 (.so),然后在 Petalinux 中打包使用。
前端·webpack·node.js
聪明的笨猪猪2 小时前
Java SE “JDK1.8新特性”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
小爱同学_2 小时前
一次面试让我重新认识了 Cursor
前端·面试·程序员
golang学习记3 小时前
AI 乱写代码?不是模型不行,而是你的 VS Code 缺了 Context!MCP 才是破局关键
前端
星光不问赶路人3 小时前
Vite 中的 import.meta.glob vs 动态导入:该用哪个?
前端·vite
疯狂踩坑人3 小时前
【万字长文】让面试没有难撕的JS基础题
javascript·面试
z_y_j2299704383 小时前
服务器中使用Docker部署前端项目
服务器·前端·docker·容器
迪丽热爱3 小时前
解决【npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本。】问题
前端·npm·node.js