【HarmonyOS - ArkTS - 状态管理】

news2025/1/18 2:09:27

概述

本文主要是从页面和应用级两个方面介绍了ArkTS中的状态管理的一些概念以及如何使用。可能本文比较粗略,细节化请前往官网(【状态管理】)学习,若有理解错误的地方,欢迎评论指正。

装饰器总览

由于下面会频繁提及到装饰器,所以来头就将会提及到了列举出来,做个简单介绍。

  • @State 定义组件的响应式数据,这个状态发生改变会触发UI更新,定义时必须要赋予一个默认值。
  • @Prop: 定义由父组件传递过来的参数,是单向的,子组件改变该值不会影父组件 父组件通过prop = this.state.prop来传值,复制的数据源
  • @Link 定义一个双向绑定的值,子组件也可以改变父组件的值,类似Vue中的v-modal,父组件使用$来传递值的引用 prop = $prop,使用的是数据源的引用
  • @Provide 在上级组件通过该装饰器定义需要共享的数据(双向的,子组件也可以更新值)
  • @Consume 上级组件通过@Provide提供了共享数据,在下级组件就可以通过@Consume来获取,其中值的key要一致。
  • @Watch 状态监听,第一次初始化的时候不会调用。@Watch不能单独使用,需要和其他装饰器一起,常见于@Prop、@State、@Link来监听状态 。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===)
  • @Observed / @ObjectLink :用于解决通过@State定义嵌套对象/数组等复杂变量时,只能通过将整体替换来触发更新的问题。其中@Observed 用来修饰class,表示该类中的属性需要被监控,然后在组件中通过@ObjectLink来接收一个@Observed 修饰的class类型的变量,这样当class中的属性变化时,就会触发UI更新。原理就是当使用@Observed 修饰一个class的时候,会对这个class进行代理,代理setter、getter。当子组件通过@ObjectLink接收该class的类型变量时,会自动将@ObjectLink本身注册到@Observed修饰的代理class中,当class的属性改变,就会通过代理来通过所有订阅该class类型变量更新。
  • $$语法:内置组件双向同步,其实就是将变量通过$$运算符传递给内置的ArkUI内置组件后,内置组件就可以改变组件内我们通过@State、@Prop、@Link定义的变量值。目前只支持Refresh组件的refreshing参数。
  • @LocalStorageProp: 单向流,复杂数据的方式,组件内部改变不会影响源LocalStorage值,只会影响并更新自身组件。
    @LocalStorageLink: 双向流,添加数据引用的方式,组件内部改变会写会到LocalStorage中并同步其他订阅(@LocalStorageProp、@LocalStorageLink)了该数据的组件更新。
  • @StorageProp、@StorageLink:同上面一样,@Prop是单向状态,@Link是双向的,用于获取AppStorage的状态

不管单向还是双向初始化时,都必须设置默认值,以便当LocalStorage/AppStorage中没有该key时,通过get能获取到正确的值(设定的默认值)

PS: 使用Link这种双向数据,需要注意UI渲染更新的成本。

页面组件(单个页面组件树的上级层共享)的状态管理

这个比较简单,就是通过@Prop、@Link的方式由父组件传递给子组件。

@Prop:单向传递,值的复制。通过this.xxx传递到子组件,子组件通过@Prop接收状态,修改prop不会影响到夫组件

@Link: 双向传递,值的引用。通过$prop的方式来将值的引用传递给子组件。子组件通过@Link的方式接收,值改变会同步父组件改变并触发渲染。

@Provide/@Consume:用于单个组件树的上下级状态共享。类比Vue中的(Provide/inject)或者React中的上下文(Context)。上层组件通过@Provide提供状态,以该组件为root的组件树中其他下层组件都可以通过@Consume来订阅需要的状态。

APP应用级别的状态管理

LocalStorage - 单个UIAbility

根据不同的注入方式,LocalStorage可以作为UIAbility级别(跨组件)的状态共享(类似Vuex),也可以作为组件内部单个树上下层共享(类似上面的@Provide、@Consume)

应用(单个UIAbility)级别状态共享 - 类似Vuex

在启动应用程序时,会先创建一个Stage,然后加载页面,在onWindowStageCreate生命周期(UIAbility的生命周期)中,将LocalStorage注入,这时候在LocalStorage所有的数据都可以跨组件共享。

// EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  para:Record<string, number> = { 'PropA': 47 };
  storage: LocalStorage = new LocalStorage(this.para);

  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index', this.storage);
    }
}

全局注入之后,在组件内通过GetShared根据key获取值然后将获取到的storage通过@Entry参数的形式注入到组件内部。

import router from '@ohos.router';

let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct Page {
  @LocalStorageLink('PropA') propA: number = 2;

  build() {
    Row() {
      Column() {
        Text(`${this.propA}`)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)

        Button("Change propA")
          .onClick(() => {
            this.propA = 100;
          })

        Button("Back Index")
          .onClick(() => {
            router.back()
          })
      }
      .width('100%')
    }
  }
}
组件上下级共享 - 类似@Provide、@Consume

LocalStorage可以创建多个,就算在创建Stage的时候注入了一个全局的LocalStorage,也可以在组件内创建一个自己的LocalStorage数据库,在组件内创建然后在组件装饰器的@Entry作为参数传入,就类似@Provide、@Consume支持以这个组件为根节点的组件树共享。

let storage = new LocalStorage({ countStorage: 1 });

@Entry('storage')
@Component
struct Child {
  // 子组件实例的名字
  label: string = 'no name';
  // 和LocalStorage中“countStorage”的双向绑定数据
  @LocalStorageLink('countStorage') playCountLink: number = 0;

  build() {
    Row() {
      Text(this.label)
        .width(50).height(60).fontSize(12)
      Text(`playCountLink ${this.playCountLink}: inc by 1`)
        .onClick(() => {
          this.playCountLink += 1;
        })
        .width(200).height(60).fontSize(12)
    }.width(300).height(60)
  }
}

AppStorage

AppStorage就是LocalStorage的一个单例,所以它所有的属性方法都是静态的,而且也可以在UI组件内部和应用级别进行状态管理。

和LocalSTorage的区别:

  • LocalStorage主要是单个UIAbility中多个组件的状态管理,而AppStorage是整个应用级别的状态管理可以用于多个UIAbility共享(一个应用可能有多个UIAbility)。
  • LocalStorage的数据需要传入@Entry装饰器进行共享,而AppStorage直接通过@StorageLink就可以获取
UI组件内部状态管理

通过@StorageProp、@StorageLink来获取值

AppStorage.SetOrCreate('count', 1);

@Entry
@Component
struct TaskPage {
  @StorageProp('count') pageCount: number = 1 // 单向,不会改变AppStorage的值
  // @StorageLink('count') pageCOunt: number = 1 双向,会改变源数据
  build()
  {
    Text('Text')
  }
}
应用级别状态共享

通过SetOrCreate创建变量之后,在所有的UIAbility都可以通过Get来获取数据的值。

AppStorage.SetOrCreate('count', 1);
AppStorage.Get('count') // 1
其他用途

AppStorage除了上面提到的可以用于组件内/应用状态管理,主要还可以和数据持久化(PersistentStorage)和环境变量(Environment)使用,只要AppStorage中的值变化,PersistentStorage和Environment中的值也会变化,新值会覆盖旧值,并且在PersistentStorage和Environment中不能创建AppStorage已经有的属性,因为每次AppStorage更新(新增/删除变量)都会同步到PersistentStorage和Environment中,所有AppStorage中有的,其他两个中自然也有。

持久化存储

上面提到的AppStorage除了状态管理之外还和PersistentStorage和Environment有关,现在介绍一下PersistentStorage。

因为AppStorage是运行时保存在内存的数据,当应用关闭之后,保存的数据就被销毁了。为了将一个数据保存在用户本地,在第二次打开应用时能直接访问,所以出现了PersistentStorage。

PersistentStorage是通过写文件的方式,将数据写入文件然后保存在用户磁盘(同步写入)达到持久化的目的。

PersistentStorage是和AppStorage双向绑定的,可以理解为PersistentStorage是AppStorage的子集,通过API来设置AppStorge的哪些属性需要保持在PersistentStorage中进行持久化存储。

当应用启动之后,会先调用PersistentStorage来获取数据,如果没有则去AppStorage中查找,如果没有就在AppStorage中创建该属性并使用默认值进行初始化,然后写回到PersistentStorage。

PersistentStorage只能保存基本的简单类型(number、string、boolean、enum),并且由于写入是同步保存在用户磁盘上的,所以大小一般小于2KB。
下面代码表示:在UI内通过PersistProp(PersistentStorage初始化值的API),然后会在AppStorage上创建相同的属性(如果没有),在组件内通过@StorageLink去获取AppStorage的值(AppStorage的值和PersistentStorage值是一致的 -> 子集)

PersistentStorage.PersistProp('aProp', 47);

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  @StorageLink('aProp') aProp: number = 48

  build() {
    Row() {
      Column() {
        Text(this.message)
        // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
        Text(`${this.aProp}`)
          .onClick(() => {
            this.aProp += 1;
          })
      }
    }
  }
}

Environment设备环境参数

Environment中保存的是当前设备的一些配置参数,比如国际化语言、暗黑模式…。
image.png
Environment只能保存简单的基本类型,并且是内置的环境参数,不可修改,只能访问。
在应用中一般是将我们需要的Environment配置注入到AppStorage中,然后在组件中进行访问。

@StorageProp: 值的复制,可以在组件内改变,但是不会写回到AppStorage中
@StorageLink:值的引用,会写回到Appstorage,但是不会改变Environment中的值,因为Environment是只读的。

数据更新链路:Environment -> AppStorage -> Components
下面的代码表示在UI组件内获取环境配置并使用。

// 将设备languageCode存入AppStorage中
Environment.EnvProp('languageCode', 'en');

@Entry
@Component
struct Index {
  @StorageProp('languageCode') languageCode: string = 'en';

  build() {
    Row() {
      Column() {
        // 输出当前设备的languageCode
        Text(this.languageCode)
      }
    }
  }
}

状态监听@Watch和$$运算符

@Watch

@Watch使用全等(===)的方式来对单个变量进行变化监控,需要传入一个callback,发生改变就会调用该函数,默认第一次初始化的时候不会调用(undefined->value)。可以类比Vue中的Watch监听。

@Watch是用于响应数据的监听,一般于@Prop、@Link、@StorageProp…等这些申明状态的修饰器连用

@Prop @Watch('callback') state: number = 0;

// changedPropertyName:传入的监听的属性名
// 一般当多个Watch监听状态并使用同一个callback处理的时候,可以通过该属性名分别处理
callbakc = (changedPropertyName: string) => {}

PS:

  • 不要在@Watch的回调中改变监听的状态值,否则会造成死循环
  • 不要在回调中通过async/await来进行异步任务,因为@Watch主要是用于快速计算逻辑,异步任务可能会导致渲染性能问题
$$ 运算符

$$ 是内置组件双向同步,因为ArkTs内置了申明式UI(ArkUI),可以通过标签的方式直接使用UI组件,但是要想获取组件内属性变量的变化,就需要手动进行监听复制,ArkTS提供的$$运算符,能实现内置组件属性和传入变量的双向绑定,可以类比Vue中的v-modal语法糖。

下面代码就是将自定义的组件状态isRefreshing和内置组件Refresh的refreshing双向绑定,内置组件的refreshing变化会同步自定义组件状态isRefreshing的变化。

@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false
  @State counter: number = 0

  build() {
    Column() {
      Text('Pull Down and isRefreshing: ' + this.isRefreshing)
        .fontSize(30)
        .margin(10)

      Refresh({ refreshing: $$this.isRefreshing, offset: 120, friction: 100 }) {
        Text('Pull Down and refresh: ' + this.counter)
          .fontSize(30)
          .margin(10)
      }
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus)
      })
    }
  }
}

PS:

  • 当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
  • 当前$$仅支持Refresh组件的refreshing参数。
  • $$绑定的变量变化时,会触发UI的同步刷新。

回顾

上面我们从页面组件级别、单个UIAbility级别、整个APP应用级别简单介绍了一下状态管理的知识,可以查看下图来梳理一下,希望对大家有所帮助。

下图主要画出了整个APP的状态流程。 从书写的Js/Ts代码逻辑 -> 启动应用调用(AppStorage\LocalStorage\Persistent) -> Components组件中状态共享 -> 父子组件共享

image.png
说个题外话,有兴趣的小伙伴可以关注一下微信公粽号【大前端小册子】,以便随时随地可以一起学习探讨。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1720092.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将三个字符串通过strcat连接起来并打印输出

将三个字符串通过strcat连接起来并打印输出 #include <stdio.h> #include <string.h> int main () { char a[10]"I", b[10]" am",c[10]" happy"; strcat(a,b); strcat(a,c); printf("%s",a); printf("\n"); re…

win10键盘按乱了,如何恢复?

今天键盘被宝宝给按乱了&#xff0c;好不容易给重新调整回来&#xff0c;记录备忘&#xff1a; 1、win10的asdf和方向键互换了&#xff1a; 使用Fnw键来回切换&#xff0c;OK&#xff01; 2、键盘的win键失效&#xff0c;例如&#xff1a;按winD无法显示桌面。此时&#xf…

Django企业招聘后台管理系统开发实战四

前言 首先我们看一下产品的需求背景&#xff0c;这个产品为了解决招聘面试的过程中&#xff0c;线下面试管理效率低&#xff0c;面试过程和结果不方便跟踪的痛点 招聘管理的系统几乎是每一家中小公司都需要的产品 我们以校园招聘的面试为例子来做 MVP 产品迭代 首先我们来看一下…

【计算机毕设】基于Spring Boot的课程作业管理系统 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 课程作业管理系统旨在为教师和学生提供一个便捷的平台&#xff0c;用于发布、提交和评定课程作业。本系统旨在提高作业管理的效率&#xff0c;促进教…

【免费Web系列】JavaWeb实战项目案例四

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 多表操作&员工列表查询 1. 多表关系 关于单表的操作(单表的设计、单表的增删改查)我们就已经学习完了。接下来我们就要来学习多表的操作&#xff0c;首先来学习多表的设计。 项目开发中&#xff0…

如何使用 Connector API 将数据提取到 Elasticsearch Serverless 中

作者&#xff1a;来自 Elastic Jedr Blaszyk Elasticsearch 支持一系列摄取方法。 其中之一是 Elastic Connectors&#xff0c;它将 SQL 数据库或 SharePoint Online 等外部数据源与 Elasticsearch 索引同步。 连接器对于在现有数据之上构建强大的搜索体验特别有用。 例如&…

618 购物指南:简单点 618 捡便宜,用这个利器就行

直接干货&#xff0c;看效果&#xff0c;安装脚本直接显示商家额外优惠券&#xff1a; 1、安装好脚本后&#xff0c;打开京东、淘宝(天猫) 商品详情页面&#xff0c;脚本会自动获取优惠券进行展示。 2、如果当前商品 处于 30 天最低价&#xff0c;脚本将自动进行标记 提醒&…

基于Python的校园预约打印网站的实现

基于Python的校园预约打印网站的实现 开发语言:Python 数据库&#xff1a;MySQL所用到的知识&#xff1a;Django框架工具&#xff1a;pycharm、Navicat、Maven 系统功能实现 注册 新用户首先要进行注册信息填写&#xff0c;填写完成以后进行登录即可使用此网站 打印社 分别有…

对称二叉树(oj题)

一、题目链接https://leetcode-cn.com/problems/symmetric-tree/ 二、题目思路 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称的思路: 1.将该树的左子树和右子树&#xff0c;当做两棵树&#xff0c;调用 判断两棵树是否对称相等的函数 2.判断两颗树是否对称相…

代码随想录算法训练营第36期DAY44

DAY44 闫氏DP 2 01背包问题 用滚动数组来优化空间&#xff0c;从后向前&#xff08;大到小&#xff09;遍历j #include<iostream>using namespace std;const int N1010;int n,m;int v[N],w[N];int f[N][N];//所有只考虑前i个物品&#xff0c;**且总体积不超过j**的选法…

MxA Protein Human ELISA--BioVendor

MxA(1)MX166276-kDaGTPase 蛋白在抗多种病毒的细胞内发挥抗病毒活性重要作用&#xff0c;包括流感、副流感、麻疹、柯萨奇、乙型肝炎病毒和后藤病毒。病毒在其生命周期的早期&#xff0c;即进入宿主细胞后不久和基因组扩增前&#xff0c;被蛋白抑制。小鼠蛋白人蛋白的小鼠类似…

华为 CANN

华为 CANN 1 介绍1.1 概述1.2 CANN 是华为昇腾计算产业的重要一环1.3 昇腾系列处理器1.4 昇腾 AI 产业1.5 从 AI 算法到产品化落地流程1.6 多样性计算架构1.7 人工智能各层级图示1.8 人工智能技术发展历史 2 CANN vs CUDA支持平台优化方向编程接口生态系统与应用性能与功能 3 C…

算法(十二)分治算法

文章目录 算法概念算法例子字符串中小写转大写求X^n问题 算法概念 分治算法&#xff08;divide and conquer&#xff09;算法的核心思想其实就是"分而治之"&#xff0c;将原问题划分成n个规模较小&#xff0c;并且结构与原问题相似的子问题&#xff0c;递归地解决这…

ADB安装教程

1 adb简介 Android 调试桥 (adb) 是一种功能多样的命令行工具&#xff0c;可让您与设备进行通信。 adb命令可用于执行各种设备操作&#xff0c;例如安装和调试应用。 adb 提供对 Unix shell&#xff08;可用来在设备上运行各种命令&#xff09;的访问权限。它是一种客户端-服务…

国外创意二维码营销案例:巴西宠物食品品牌户外活动“救救宠物爪子吧”

2024年5月份&#xff0c;巴西宠物食品品牌Purina Brasil 与广告公司Publicis Brasil合作&#xff0c;推出了一次特别的户外营销活动——Salve as Patinhas(Save the Paws)&#xff08;救救宠物爪子吧&#xff09;&#xff0c;非常有意思&#xff01; 随着全球气候变暖&#xf…

大坝安全监测自动化技术的规范化设计准则

大坝安全监测自动化技术的规范化设计准则 一、施工阶段自动化系统设计要点 在施工阶段&#xff0c;大坝安全监测自动化系统的设计应当涵盖以下几个核心内容&#xff1a; 监测仪器的布局规划及详细的施工图纸设计。 配套土建项目以及防雷设施的施工设计规划。 明确施工过程中的技…

职场中,那些35岁以上的测试猿到底去哪了?

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

电商物流查询解决方案助力提升消费者体验

截至2023年12月&#xff0c;中国网络购物用户规模达9.15亿人&#xff0c;占网民整体的83.8%。这一庞大的数字不仅展现了电子商务的蓬勃发展&#xff0c;也标志着数字零售企业营销战略的转变——从以产品和流量为核心&#xff0c;到用户为王的新阶段。因此&#xff0c;提升消费者…

MySQL--MHA高可用及读写分离

一、什么是高可用 1.企业级高可用标准&#xff1a;全年无故障时间 全年无故障时间全年故障时间具体时间99.9%0.1%525.6 minkeeplive双主 &#xff08;切换需要人为干预&#xff09;99.99%0.01%52.56 minMHA &#xff08;半自动化&#xff09;99.999%0.001%5.256 minPXC、MGR、…

利用映射算子打印菱形

文章目录 一、利用RDD完成&#xff08;一&#xff09;右半菱形&#xff08;二&#xff09;左半菱形&#xff08;三&#xff09;完整菱形&#xff08;四&#xff09;输出任意大菱形 二、利用Java完成&#xff08;一&#xff09;右半菱形&#xff08;二&#xff09;左半菱形&…