HarmonyOS 角落里的知识 —— 状态管理

一、前言

在探索 HarmonyOS 的过程中,我们发现了许多有趣且实用的功能和特性。有些总是在不经意间或者触类旁通的找到。或者是某些开发痛点。其中,状态管理是ArkUI开发非常核心的一个东西,我们进行了大量的使用和测试遇到了许多奇奇怪怪的问题。

该系列将着重分享、介绍HarmonyOS API11+的新版本特性或者奇奇怪怪的解决方案、BUG。(弃用API非必要不提及)

二、@State

这应该是用的最多的了,状态变量,将状态变量和UI的某个属性绑定,即可同步两者。

初始化行为

组件初始化时,@State只能从外面传入一次,之后父组件值的修改并不会影响到组件内部的@State,所以它叫组件内状态~(当然你组件用if、else来更新当我没说)

复制代码
 @Entry
 @Component
 export struct ExpComponent {
     @State title: string = "Title"
 ​
     build() {
         Column() {
             ExpChildComponent({
                 childTitle: this.title
             })
             Button(this.title)
                 .onClick(()=>{
                     this.title = "Click"
                 })
         }
     }
 }
 @Component
 export struct ExpChildComponent {
     @State childTitle: string = "????"
 ​
     build() {
         Text(this.childTitle)
     }
 }
  1. 就像这样,onClick触发后ExpChildComponent并不会发生改变了。

太多的@State

  1. 一开始我们会直接将状态变量直接声明在组件中,如:
复制代码
 @Component
 export struct ExpComponent {
     @State title: string = ""
 ​
     build() {
         Text(this.title)
     }
 }

但是随着,一个页面开始变得复杂的时候,我们会面临一堆的Controller,xxState等等,看起来就很头疼。这时候利用@State能观察到Object.keys(自身)返回的所有属性的特性,我们可以抽出一个ExpUIState

复制代码
 @Component
 export struct ExpComponent {
     @State uiState: ExpUIState = new ExpUIState()
 ​
     build() {
         Text(this.uiState.title)
     }
 }
 ​
 class ExpUIState {
     title: string = ""
 }
  1. 如此,还你一个优雅的组件。

数组

@State能直接修饰数组:

复制代码
 @State title: Model[] = [new Model(1), new Model(2)];

数组自身的赋值可以观察到。

复制代码
 this.title = [new Model(2)];

数组项的赋值可以观察到。

复制代码
 this.title[0] = new Model(2);

删除数组项可以观察到。

复制代码
 this.title.pop();

新增数组项可以观察到。

复制代码
 this.title.push(new Model(12));

数组项中属性的赋值观察不到。

复制代码
this.title[0].value = 6;
  • 数组对象及其子项的赋值是可以被观察到的,所以"titles[0].value = 1 "这种使用方法是没用滴。

    4.作用域的影响

  • 得益于闭包的特性(对于Java、Kotlin开发来说不存在的东西)箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。

    复制代码
    @Component
    export struct ExpComponent {
        @State uiState: ExpUIState = new ExpUIState()
    
        build() {
            Column() {
                Text(this.uiState.title)
                Button("Click").onClick(()=>{
                    this.uiState.autoRefreshTitle()
                })
            }
        }
    }
    class ExpUIState {
        title: string = "??"
        autoRefreshTitle = () => {
            this.title = "AutoRefreshTitle"
        }
    }

    上述操作,触发onClick是不会更新UI的,如果想要触发更新需要用下面的例子:

    @Component
    export struct ExpComponent {
    @State uiState: ExpUIState = new ExpUIState()

    复制代码
      aboutToAppear(): void {
      }
    
      build() {
          Column() {
              Text(this.uiState.title)
              Button("Click").onClick(()=>{
                  let fk = this.uiState
                  this.uiState.autoRefreshTitle(fk)
              })
          }
      }

    }
    class ExpUIState {
    title: string = "??"
    autoRefreshTitle = (st:ExpUIState) => {
    st.title= "AutoRefreshTitle"
    }
    }

  1. 反人类吧?阿弥陀佛。

嵌套对象

@State修饰对象时,里面可能还有对象,或者数组.

复制代码
class ExpUIState {
    childs: ExpChild[] = []
    firstChild: ExpChild = new ExpChild()
}
  1. 很可惜的是,由于@State装饰的变量,只能监听到对象本身的地址以及第一层属性的地址变化,也就是firstChild或者childs地址的变化,例如如下操作:uiState.firstChild.subTtile="",uiState.childs.push(new ExpChild())等是没法触发刷新的。

    解决办法就是不用@State

三、@Prop

初始化行为

几乎和@State一致,但是@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。

相比起@State重点就是父组件修改后子组件会同步。苦恼@State没法修改的人,有福了~
2.

套一堆的@Prop

不要这么做。如果你的子组件使用@Prop、孙子组件也用@Prop,我其实推荐你使用@Provide(当然你硬要用也行)
3.

@Require

使用@Prop有一个好处就是,可以使用@Require。变成一个必传的参数(福音啊)。@State是可以不传的。

复制代码
@Require @Prop index: number 

Observed

坏消息:@Prop也没法观察嵌套对象~,因为父组件传入时,用的@State传入的。好消息:加一个@Observed就好了~

复制代码
@Component
export struct ExpComponent {
    @State uiState: ExpUIState = new ExpUIState()

    aboutToAppear(): void {
    }

    build() {
        Column() {
            ExpChildComponent({
                child: this.uiState.firstChild
            })
            Button("Click").onClick(() => {
                this.uiState.firstChild.subTitle = "????"
            })
        }
    }
}

@Component
export struct ExpChildComponent {
    @Require @Prop child: ExpChild

    build() {
        Text(this.child.subTitle)
    }
}


@Observed
class ExpUIState {
    childs: ExpChild[] = []
    firstChild: ExpChild = new ExpChild()
}

@Observed
class ExpChild {
    subTitle: string = "啊"
}

在嵌套场景下,每一层都要用@Observed装饰,且每一层都要被@Prop接收。

  1. 初始化行为

    @Link装饰的变量与其父组件中的数据源共享相同的值

    PS:从API version 9开始,@Link子组件从父组件初始化@State的语法为Comp({ aLink: this.aState })。同样Comp({aLink: $aState})也支持(早该如此了)

  2. 给Dialog用

    如果你在某个Dialog中,想同步数据到组件,@Link是一个很好的选择:

    @CustomDialog
    struct CustomDialog01 {
    @Link inputValue: string;
    controller: CustomDialogController;

    build() {
    Column() {
    Text('Change text')
    .fontSize(20)
    .margin({ top: 10, bottom: 10 })
    TextInput({ placeholder: '', text: this.inputValue })
    .height(60)
    .width('90%')
    .onChange((value: string) => {
    this.inputValue = value;
    })
    }
    }
    }

    @Entry
    @Component
    struct DialogDemo01 {
    @State inputValue: string = 'click me';
    dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialog01({
    inputValue: $inputValue
    })
    })

    build() {
    Column() {
    Button(this.inputValue)
    .onClick(() => {
    this.dialogController.open();
    })
    .backgroundColor(0x317aff)
    }
    .width('100%')
    .margin({ top: 5 })
    }
    }

  3. 当然,你也可以传个函数进去~

  4. @Link套娃

    就像@Prop一样,我很建议你使用@Provide@Consume

五、@Watch

@Watch应用于对状态变量的监听,这个装饰器可以说是非常直观的一个了,就是用来观察状态变量的。

不要在里面修改状态变量

复制代码
 @State @Watch("onUiStateChange") uiState: ExpUIState = new ExpUIState()
 onUiStateChange(){
     this.uiState.firstChild = new ExpChild()
 }
  • 相信你看得出来,这是一个死循环吧?

子组件事件传递到父组件

可通过状态同步@Link@Provide@Consume进行父子组件间的状态通知,结合@Watch可以将状态变量的修改在组件间传递,实现类似的功能。
*

粘性

@Watch没有粘性,所以第一次初始化并不会触发@Watch!
*

日志

@Watch可以观察状态变量变化的特性,很适合来做Log日志不是吗~

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。

鸿蒙HarmonyOS Next全套学习资料←点击领取!(安全链接,放心点击

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了**(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)**技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类...等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

HarmonyOS Next 最新全套视频教程

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .......

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ......

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ......

大厂面试必问面试题

鸿蒙南向开发技术

鸿蒙APP开发必备

鸿蒙生态应用开发白皮书V2.0PDF

获取以上完整鸿蒙HarmonyOS学习资料,请点击→****

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。

相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰6 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪6 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy7 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom8 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom8 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试