灌篮少年 发表于 4 天前

鸿蒙的状态管理

任何语言都有本身的声明数据的方式,好比C语言中 int a=1;JavaScript中let arr=
这篇文章主要探讨鸿蒙中的数据声明以及数据通报。
鸿蒙的底子知识可以参考:Harmony OS NEXT初学笔记-CSDN博客

一.@state与private


@Entry
@Component
struct Index {
@State message: string = 'Hello World';
private str="你好"
build() {
    Column() {
      Text(`message:${this.message}`)
      Text(`str:${this.str}`)
      Button("按钮")
      .onClick(()=>{
          this.message="你好世界"
          this.str="hello "
          console.log(`点击按钮后,message:${this.message},str:${this.str}`)
      })
    }
    .height('100%')
    .width('100%')
}
} 在这部分代码中用@State和private声明了两个变量,下面看效果
https://i-blog.csdnimg.cn/direct/397d76161d4247e1b8f2c3c906797cda.png
在页面上表现出来了,再看按钮,绑定了点击事件,点击按钮改变两个声明的变量,下面我们点击按钮,看看效果
https://i-blog.csdnimg.cn/direct/d910b0b025b34189aa84b30e495c6f21.png
https://i-blog.csdnimg.cn/direct/0e27164532534ec7a46ccac6889846b9.png
发现两个变量的值都改变了,但是在页面上只改变了用@state声明的值

那么我们可以得出结论:用@state声明的状态变量改变后,页面会随之改变,但是用private声明的变量,大概不用private声明直接写变量的比方 num=1,这种改变不会使页面革新但是这种变量的值也确实会改变

那么我们用private声明的值盘算,把值赋给@State声明的呢?

@Entry
@Component
struct Index {
@State message: string = 'Hello World';
private str="你好"
@State sum:number=0
private num1:number=0
private num2:string="0"
build() {
    Column() {
      Text(`message:${this.message}`)
      Text(`str:${this.str}`)
      Button("按钮")
      .onClick(()=>{
          this.message="你好世界"
          this.str="hello "
          console.log(`点击按钮后,message:${this.message},str:${this.str}`)
      })
      TextInput({placeholder:"请输入num1"}).onChange((value)=>{
      this.num1=Number(value)
      console.log(`nmum1:${this.num1.toString()}`)
      })
      TextInput({placeholder:"请输入num2",text:$$this.num2})
      Text(`num1+num2= ${this.num1+Number(this.num2)}`)
      Text(`sum= ${this.sum}`)
      Button("按钮").onClick(()=>{
      this.sum=this.num1+Number(this.num2)
      console.log(`num1+num2= ${this.num1+Number(this.num2)}`)
      })
    }
    .height('100%')
    .width('100%')
}
} 这里我们声明两个变量举行输入,注意到num1是用的textinput的onchange函数来赋值,而num2则是用$$举行双向绑定,在这里$$相称于onchange的简写
那么我们看看效果
https://i-blog.csdnimg.cn/direct/8666e2aca7014f6bb5d79f0a906f6bae.png
发现,两个private数据相加也不会改变,但是点击按钮后会触发@State声明的变量改变来触发页面革新
https://i-blog.csdnimg.cn/direct/dc351ad0e30f41779ab2f245d23548ee.png
https://i-blog.csdnimg.cn/direct/72674401308a49028178873307164d03.png
同样有输出效果

那么我们想一想,现在是每次点击按钮页面才会革新,那么有没有一种方法不用点击按钮自动革新呢?
那就是我们下面介绍的

二.@Watch

由于@Watch必须与装饰器绑定,因此@Watch不能装饰private

@Entry
@Component
struct Index {
@State message: string = 'Hello World';
private str="你好"
@State sum:number=0
private num1:number=0
@State @Watch("change") num2:string="0"
change(){
    this.sum=this.num1+Number(this.num2)
}
build() {
    Column() {
      Text(`message:${this.message}`)
      Text(`str:${this.str}`)
      Button("按钮")
      .onClick(()=>{
          this.message="你好世界"
          this.str="hello "
          console.log(`点击按钮后,message:${this.message},str:${this.str}`)
      })
      TextInput({placeholder:"请输入num1"}).onChange((value)=>{
      this.num1=Number(value)
      console.log(`nmum1:${this.num1.toString()}`)
      })
      TextInput({placeholder:"请输入num2",text:$$this.num2})
      Text(`num1+num2= ${this.num1+Number(this.num2)}`)
      Text(`sum= ${this.sum}`)
      Button("按钮").onClick(()=>{
      this.sum=this.num1+Number(this.num2)
      console.log(`num1+num2= ${this.num1+Number(this.num2)}`)
      })
    }
    .height('100%')
    .width('100%')
}
} 注意看,我们在@Watch后面加了一个change函数,也是必须加的,名字无所谓,每次改变num2的值会实行change函数,那么,现在我们每次改变输入num2的值就会重新盘算sum,也就不需要点击按钮了。效果如下。
https://i-blog.csdnimg.cn/direct/b7680ac109314034a305a4f686fbb648.png
除此之外,还意外发现了num1+num2也变化了,那也就是:private声明的与@State声明的值举行操作会改变页面,触发页面革新??

三.父子通报

页面之间的跳转携带参数我们知道是在router的params内里,那么现在,假如两个Component要通报通报参数呢?
下面先介绍一种通用的方法,也就是get set,本质上是通过一个中心人,这种方法实用于绝大部分的数据通报
起首新建一个类:
export class MyStore{
static str:string

static setStr(str:string){
    MyStore.str=str
}
static getStr(){
    return MyStore.str
}
} 再来看set:
import { MyStore } from '../store/myStore';
import { router } from '@kit.ArkUI';

@Entry
@Component
struct GetAndSet {
@State message: string = 'Hello World';

build() {
    Column() {
      Button("Set").onClick(()=>{
      MyStore.setStr(this.message)
      })
      Button("Router").onClick(()=>{
      router.pushUrl({
          url:"pages/Get"
      })
      })
      Button("ReSet").onClick(()=>{
      MyStore.setStr("123")
      })
    }
    .height('100%')
    .width('100%')
}
} 在这里,我们利用set,把message的值赋给了对象中的str
再来看get
import { MyStore } from '../store/myStore';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Get {
@State message: string = 'Hello World';

build() {
    Column() {
      Button("Get").onClick(()=>{
      promptAction.showToast({
          message:`${MyStore.getStr()}`
      })
      setTimeout(()=>{
          AlertDialog.show({
            message:`${MyStore.getStr()}`
          })
      },2000)
      })
    }
    .height('100%')
    .width('100%')
}
} 在这边我们取出了str的值。
这种get/set的模式和鸿蒙的长期化存储中的首选项雷同

那么有没有简单一点的?固然有!!!
@Prop父子单向通报

不多说,先上代码
@Entry
@Component
struct PropAndLink {
@State message: string = 'Hello World';
@State num:number=1
build() {
    Column() {
      Column() {
      Text("我是主页面")
      Text(`num= ${this.num}`)
      Button("主页面的按钮").onClick(()=>{
          this.num++
      })
      }.height('50%')
      .width('100%')
      Column(){
      child({num:this.num})
      }.height('50%')
      .width('100%')
    }.height('100%')
    .width('100%')
}
}

@Component
export struct child {
@Prop num:number
build() {
    Column() {
      Text("我是子页面")
      Text(`num= ${this.num}`)
      Button("子页面的按钮").onClick(()=>{
      this.num++
      })
    }
}
} 在这个页面有两个component,在主页面上表现效果如下:
https://i-blog.csdnimg.cn/direct/f1d1883f35574b1d8a043cabce1f2172.png
在两个component中,都分别有一个按钮,都是给对应的component中的num+1,
现在我们点击下面的按钮两下:
https://i-blog.csdnimg.cn/direct/01fd4f3be6fc4048a0f8dcd0380f71db.png
发现,只有下面的变化了,上面的不会变化,
那么我们在点击上面的按钮一下:
https://i-blog.csdnimg.cn/direct/2b74f1c2140546f697e113370b02b544.png
发现上下都酿成2了,下面酿成2是由于上面的酿成2之后,把2传给了下面,因此下面才表现2
由此我们得出结论:@Prop修饰的变量只能父组件改变子组件,在子组件改变不影响父组件的改变。

注意:@Prop的传值方式,调用组件的时候传值,以对象的情势:像这里的
child({num:this.num})
@Link 父子双向通报

一样,先上代码:
@Entry
@Component
struct PropAndLink {
@State message: string = 'Hello World';
@State num: number = 1
@State num2: number = 100

build() {
    Column() {
      Column() {
      Text("我是主页面")
      Text(`num= ${this.num}`)
      Button("主页面的按钮1").onClick(() => {
          this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("主页面的按钮12").onClick(() => {
          this.num2++
      })
      }.height('50%')
      .width('100%')

      Column() {
      child({ num: this.num, num2: this.num2 })
      }.height('50%')
      .width('100%')
    }.height('100%')
    .width('100%')
}
}

@Component
export struct child {
@Prop num: number
@Link num2: number

build() {
    Column() {
      Text("我是子页面")
      Text(`num= ${this.num}`)
      Button("子页面的按钮").onClick(() => {
      this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("子页面的按钮12").onClick(() => {
      this.num2++
      })
    }
}
} 检察效果:
https://i-blog.csdnimg.cn/direct/ba1e605047324eb282d3f9b1bb7f8575.png
我们先点击下面的按钮:
https://i-blog.csdnimg.cn/direct/e70b3bb309594946abc479c62f32eee0.png
发现父组件,子组件都发生改变,
我们再点击父组件的按钮
https://i-blog.csdnimg.cn/direct/08188f7118c84162aba0f6190489ff98.png
发现也是,父组件改变,子组件也改变

因此我们得出结论:@Link修饰的变量可以在父组件改变子组件的值,也可以在子组件改变父组件的值,也就是父子双向通报

那么我们思索,有父子,有没有爷孙呢?但是本质上来说刚刚已经讲过了,也就是get/set方法,不过,我们介绍更简单方便的。

@Provide装饰器和@Consume装饰器:与后代组件双向同步

实在可不可以不用这两个,固然可以,我们给出以下例子
@Entry
@Component
struct GrandFather {
@State message: string = 'Hello World';

build() {
    Column() {
      Column() {
      Text("GrandFather")
      }.layoutWeight(1)

      Father().layoutWeight(1)
    }
    .height('100%')
    .width('100%')
}
}

@Component
export struct Father {
build() {
    Column() {
      Column() {
      Text("Father")
      }.layoutWeight(1)

      Son().layoutWeight(1)
    }.width("100%")
    .height("100%")
}
}

@Component
export struct Son {
build() {
    Column() {
      Text("Son")
    }
}
}
这是一个最根本的爷孙类型的组件,来看页面效果:
https://i-blog.csdnimg.cn/direct/fd93b359e18d41c7ac3b6840a36f9d65.png
下面我们在内里声明数据:
@Entry
@Component
struct GrandFather {
@State message: string = 'Hello World';
@State num: number = 1

build() {
    Column() {
      Column() {
      Text("GrandFather")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
          this.num++
      })
      }.layoutWeight(1)

      Father({ num: this.num }).layoutWeight(1)
    }
    .height('100%')
    .width('100%')
}
}

@Component
export struct Father {
@Link num: number

build() {
    Column() {
      Column() {
      Text("Father")
      Text(`num= ${this.num}`)
      }.layoutWeight(1)

      Son({ num: this.num }).layoutWeight(1)
    }.width("100%")
    .height("100%")
}
}

@Component
export struct Son {
@Link num: number

build() {
    Column() {
      Text("Son")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
      this.num++
      })
    }
}
}
我们利用father这个组件做一个桥梁,吧爷爷Grandfather和孙子Son连接起来了
效果如下:
https://i-blog.csdnimg.cn/direct/2850f2cce8ea4038954e95e3e27a1c02.png
这就是初始情况,
我们点击一下爷爷的按钮:
https://i-blog.csdnimg.cn/direct/4ac7b8fc59a84c43b68a2a9d41467003.png
发现孙子的也在变化,那么我们点击孙子的按钮:
https://i-blog.csdnimg.cn/direct/64f554ad25e5417eb523b80cfab4368a.png
发现爷爷也变化了,实在也正常,毕竟@link是双向通报嘛

那么有没有简单易达的办法,固然有咯,那就是@Provide和@Consume
先看代码:
@Entry
@Component
struct GrandFather {
@State message: string = 'Hello World';
@State num: number = 1
@Provide num2: number = 100

build() {
    Column() {
      Column() {
      Text("GrandFather")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
          this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("按钮").onClick(() => {
          this.num2++
      })
      }.layoutWeight(1)

      Father({ num: this.num }).layoutWeight(1)
    }
    .height('100%')
    .width('100%')
}
}

@Component
export struct Father {
@Link num: number

build() {
    Column() {
      Column() {
      Text("Father")
      Text(`num= ${this.num}`)
      }.layoutWeight(1)

      Son({ num: this.num }).layoutWeight(1)
    }.width("100%")
    .height("100%")
}
}

@Component
export struct Son {
@Link num: number
@Consume num2: number

build() {
    Column() {
      Text("Son")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
      this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("按钮").onClick(() => {
      this.num2++
      })
    }
}
}
实在也就是在GrandFather和Son对应的加了@provide和@consume,由爷爷提供(provide)由孙子吸收(consume)
下面给出初始状态,点击爷爷的按钮,点击孙子的按钮三种操作
https://i-blog.csdnimg.cn/direct/be56ac2a412d4b45ade7b28b96797037.png
https://i-blog.csdnimg.cn/direct/3be2b35ed1eb48d99aed1a4bbd069bc4.png
https://i-blog.csdnimg.cn/direct/e61c67d2781f4ca09bb2f7b7d45425d5.png
发现和@link一样是双向绑定的

那么这个时候有同砚就要问了,老师老师,你给的数据类型都太简单了,有没有复杂一点的,好比对象,看看这些能不能做到双向通报。
好,那就满足这位同砚的好奇心
export interface Person {
name: string,
age: number
}

@Entry
@Component
struct GrandFather {
@State message: string = 'Hello World';
@State num: number = 1
@Provide num2: number = 100
@Provide person: Person = { name: "张三", age: 20 }

build() {
    Column() {
      Column() {
      Text("GrandFather")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
          this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("按钮").onClick(() => {
          this.num2++
      })
      Text(`我叫${this.person.name},年龄是${this.person.age}`)
      Button("按钮").onClick(() => {
          this.person.name = "李四"
          //this.person={name:"李四",age:30}
      })
      }.layoutWeight(1)

      Father({ num: this.num }).layoutWeight(1)
    }
    .height('100%')
    .width('100%')
}
}

@Component
export struct Father {
@Link num: number

build() {
    Column() {
      Column() {
      Text("Father")
      Text(`num= ${this.num}`)
      }.layoutWeight(1)

      Son({ num: this.num }).layoutWeight(1)
    }.width("100%")
    .height("100%")
}
}

@Component
export struct Son {
@Link num: number
@Consume num2: number
@Consume person: Person

build() {
    Column() {
      Text("Son")
      Text(`num= ${this.num}`)
      Button("按钮").onClick(() => {
      this.num++
      })
      Text(`num2= ${this.num2}`)
      Button("按钮").onClick(() => {
      this.num2++
      })
      Text(`我叫${this.person.name},年龄是${this.person.age}`)
      Button("按钮").onClick(() => {
      this.person = { name: "王五", age: 40 }
      })
    }
}
}
这边声明一个Person类,在爷爷组件中赋初值,然后按钮改变,通报给孙子组件,在孙子组件中也可以改变,让我们同样看看三种效果:
https://i-blog.csdnimg.cn/direct/b5c25f70a15d4528a440fb9c6b829bbb.png
https://i-blog.csdnimg.cn/direct/3140ede6a7f940c79954791e5e953fb8.png
https://i-blog.csdnimg.cn/direct/5287c8f3c56b49f796f6d34c35861f75.png
我们看到,这种对象也能被捕捉,同样可以或许举行双向通报

那么复杂的数据类型呢,对象内里的某一个属性是另一个对象?
那么就是:
@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

先看代码:
class Person {
public username: string
public userage: number

constructor(username: string, userage: number) {
    this.username = username;
    this.userage = userage;
}
}

@Entry
@Component
struct ObjectLinkLayout {
@State message: string = 'Hello World'
@State P: Person = new Person("张三", 18);

build() {
    Column() {
      Row() {
      Text(`多层级数据${this.P.userage}`)
      }

      MyChild5({ P: $P })


    }.width("100%")
    .height("100%")
}
}

@Component
export default struct MyChild5 {
@Link P: Person;
build() {
    Row() {
      Text(`父组件的数据为${this.P.userage}`)
      Button("修改父组件数据").onClick(() => {
      this.P.userage++;
      })
    }
}
} 这是一个最简单的父子的通报对象的,那么我们变复杂一点
@Observed
class Info {
public info: number = 0;

constructor(info: number) {
    this.info = info;
}
}

@Component
struct ObjectLinkChild {
@ObjectLink testNum: Info;

build() {
    Text(`ObjectLinkChild testNum ${this.testNum.info}`)
      .onClick(() => {
      // 可以对ObjectLink装饰对象的属性赋值
      this.testNum.info = 47;
      })
}
}

@Entry
@Component
struct Parent {
@State testNum: Info[] = ;

build() {
    Column() {
      Text(`Parent testNum ${this.testNum.info}`)
      .onClick(() => {
          this.testNum.info += 1;
      })

      ObjectLinkChild({ testNum: this.testNum })
    }
}
} 父组件中是一个对象数组,那么我们点击子组件对应的值会相对应的变化
https://i-blog.csdnimg.cn/direct/e1ae7a6d026743bfb624401875b8210e.png
说实话,感觉这个用的并不多,也就没有详细写

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙的状态管理