
#星计划#【愚公系列】2023年12月 HarmonyOS教学课程 003-ArkTS语言(状态管理) 原创
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
文章目录
- 🚀前言
- 🚀一、ArkTS语言状态管理
- 🔎1.概述
- 🦋1.1 基本概念
- 🦋1.2 装饰器总览
- ☀️1.2.1 管理组件拥有的状态
- 🌈1.2.1.1 @State 组件内状态
- 🌈1.2.1.2 @Prop 父子单向同步
- 🌈1.2.1.3 @Link 父子双向同步
- 🌈1.2.1.4 @Provide/@Consume 与后代组件双向同步
- 🌈1.2.1.5 @Observed/@ObjectLink 嵌套类对象属性变化
- ☀️1.2.2 管理应用拥有的状态
- 🌈1.2.2.1 LocalStorage:页面级UI状态存储
- 🌈1.2.2.2 AppStorage:AppStorage
- 🌈1.2.2.3 PersistentStorage:持久化存储UI状态
- 🌈1.2.2.4 Environment:设备环境查询
- ☀️1.2.3 其他状态管理功能
🚀前言
状态管理是指在应用程序中维护和更新应用程序状态的过程。在一个程序中,可能有很多不同的组件和模块,它们需要共享和相互作用的状态。如果没有一个明确的方式来管理这些状态,就会导致代码混乱、不易维护和难以扩展。
状态管理的目标是提供一种机制,使得所有的组件和模块都可以访问和更新同一个状态。这个状态通常是存储在一个中央存储区域中,被称为状态存储或状态容器。状态管理通常与应用程序的响应式设计紧密相连,以便在状态改变时自动更新应用程序的界面。
🚀一、ArkTS语言状态管理
🔎1.概述
在声明式UI编程框架中,应用程序的UI是由程序状态驱动的。用户构建一个UI模型,其中应用的运行时状态作为参数传递进去。当参数改变时,UI会根据新的参数重新渲染。这个运行时状态的变化是由状态管理机制来处理的,它会监控状态的变化,并自动更新UI的渲染。在ArkUI中,自定义组件的变量必须被装饰器装饰为状态变量,这样它们的改变才能引起UI的重新渲染。如果不使用状态变量,UI只能在初始化时渲染,后续将不会再刷新。状态变量和UI之间的关系如下图所示:
-
View(UI):UI渲染,指将build方法内的UI描述和@Builder装饰的方法内的UI描述映射到界面。
-
State:状态,指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。
🦋1.1 基本概念
🦋1.2 装饰器总览
ArkUI提供了多种装饰器主要分为:管理组件拥有的状态、管理应用拥有的状态、其他状态管理功能,主要图形如下:
☀️1.2.1 管理组件拥有的状态
装饰器 | 功能 | 作用 |
---|---|---|
@State | 管理组件拥有的状态 | 作为子组件单向和双向同步的数据源,引起相关组件的渲染刷新 |
@Prop | 建立单向同步关系 | 可以和父组件建立单向同步关系,是可变的,但修改不会同步回父组件 |
@Link | 构建双向同步关系的状态变量 | 和父组件构建双向同步关系的状态变量,父组件会接受来自该装饰的变量的修改的同步,更新也会同步给该装饰的变量 |
@Provide/@Consume | 同步状态变量跨组件层级 | 用于跨组件层级同步状态变量,通过别名或属性名绑定 |
@Observed | 观察多层嵌套场景的class | 观察多层嵌套场景的class需要被装饰,需要和@ObjectLink、@Prop连用 |
@ObjectLink | 接收被@Observed装饰的class实例 | 应用于观察多层嵌套场景,和父组件的数据源构建双向同步 |
🌈1.2.1.1 @State 组件内状态
特点 | 描述 |
---|---|
私有性 | @State装饰的变量只能从组件内部访问。 |
声明式和类型初始化 | 在声明时必须指定其类型和本地初始化。可以使用命名参数机制从父组件完成初始化。 |
数据同步 | @State装饰的变量与子组件中的@Prop、@Link或@ObjectLink装饰变量之间建立单向或双向数据同步。 |
生命周期与所属自定义组件的生命周期相同 | @State装饰的变量生命周期与其所属自定义组件的生命周期相同。 |
@State变量装饰器只支持Object、class、string、number、boolean、enum类型,以及这些类型的数组。不支持复杂类型(比如Date类型)
父子组件初始化和传递装饰图如下:
🍬1.2.1.1.1 变化规则
1、可变类型(boolean、string、number)
2、可变类型(class、Object)
3、可变类型(array)
🍬1.2.1.1.2 使用场景
1、简单类型
2、其他类型
🌈1.2.1.2 @Prop 父子单向同步
特点 | 描述 |
---|---|
同步关系 | 通过@Prop装饰的变量和父组件建立单向的同步关系 |
在本地修改 | @Prop变量允许在本地修改,但修改后的变化不会同步回父组件 |
自动更新 | 当父组件中的数据源更改时,与之相关的@Prop装饰的变量都会自动更新 |
覆盖 | 如果子组件已经在本地修改了@Prop装饰的相关变量值,而在父组件中对应的@State装饰的变量被修改后,子组件本地修改的@Prop装饰的相关变量值将被覆盖 |
限制条件 | @Prop修饰复杂类型时是深拷贝,在拷贝的过程中除了基本类型、Map、Set、Date、Array外,都会丢失类型。@Prop装饰器不能在@Entry装饰的自定义组件中使用。 |
@Prop变量装饰器只支持string、number、boolean、enum类型,以及这些类型的数组。不支持复杂类型(比如any类型)
父子组件初始化和传递装饰图如下:
🍬1.2.1.2.1 变化规则
1、简单类型
对于@State和@Prop的同步场景:
-
使用父组件中@State变量的值初始化子组件中的@Prop变量。当@State变量变化时,该变量值也会同步更新至@Prop变量。
-
@Prop装饰的变量的修改不会影响其数据源@State装饰变量的值。
-
除了@State,数据源也可以用@Link或@Prop装饰,对@Prop的同步机制是相同的。
-
数据源和@Prop变量的类型需要相同。
🍬1.2.1.2.2 使用场景
1、父组件@State到子组件@Prop简单数据类型同步
2、父组件@State数组项到子组件@Prop简单数据类型同步
3、从父组件中的@State类对象属性到@Prop简单类型的同步
4、@Prop本地初始化不和父组件同步
🌈1.2.1.3 @Link 父子双向同步
父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步。
@Link 变量装饰器只支持string、number、boolean、enum类型,以及这些类型的数组。不支持复杂类型(比如any类型)
父子组件初始化和传递装饰图如下:
🍬1.2.1.3.1 变化规则
-
当装饰的数据类型为boolean、string、number类型时,可以同步观察到数值的变化。
-
当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性。
-
当装饰的对象是array时,可以观察到数组添加、删除、更新数组单元的变化。
🍬1.2.1.3.2 使用场景
1、简单类型和类对象类型的@Link
2、数组类型的@Link
🌈1.2.1.4 @Provide/@Consume 与后代组件双向同步
特点 | 描述 |
---|---|
双向数据同步 | @Provide和@Consume应用于与后代组件的双向数据同步 |
状态数据传递 | @Provide和@Consume应用于状态数据在多个层级之间传递的场景 |
解除参数传递 | @Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递 |
绑定关系 | @Provide和@Consume通过相同的变量名或者相同的变量别名绑定时,@Provide修饰的变量和@Consume修饰的变量是一对多的关系 |
限制条件 | 不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的@Provide装饰的变量 |
@Prop变量装饰器只支持string、number、boolean、enum类型,以及这些类型的数组。不支持复杂类型(比如any类型)
父子组件初始化和传递装饰图如下:
🍬1.2.1.4.1 变化规则
-
当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
-
当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。
-
当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。
🍬1.2.1.4.2 使用场景
🌈1.2.1.5 @Observed/@ObjectLink 嵌套类对象属性变化
特点 | 描述 |
---|---|
双向数据同步 | @ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步 |
观察属性变化 | 被@Observed装饰的类,可以被观察到属性的变化 |
双向数据绑定 | 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定 |
实例类型 | 这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰 |
搭配使用 | 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用 |
限制条件 | 使用@Observed装饰class会改变class原始的原型链,@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用 |
类型必须是@Observed装饰的class,可用于初始化常规变量、@State、@Link、@Prop、@Provide
嵌套类对象装饰图如下:
🍬1.2.1.5.1 变化规则
🍬1.2.1.5.2 使用场景
1、嵌套对象
2、对象数组
3、二维数组
☀️1.2.2 管理应用拥有的状态
存储方式 | 描述 |
---|---|
LocalStorage | 页面级UI状态存储,通常用于UIAbility内、页面间的状态共享 |
AppStorage | 特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储 |
PersistentStorage | 持久化存储UI状态,通常和AppStorage配合使用,选择AppStorage存储的数据写入磁盘,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同 |
Environment | 应用程序运行的设备的环境参数,环境参数会同步到AppStorage中,可以和AppStorage搭配使用 |
🌈1.2.2.1 LocalStorage:页面级UI状态存储
🍬1.2.2.1.1 变化规则
装饰器 | 描述 |
---|---|
@LocalStorageProp | @LocalStorageProp装饰的变量和与LocalStorage中给定属性建立单向同步关系 |
@LocalStorageLink | @LocalStorageLink装饰的变量和在@Component中创建与LocalStorage中给定属性建立双向同步关系 |
限制条件 | LocalStorage创建后,命名属性的类型不可更改。后续调用Set时必须使用相同类型的值 LocalStorage是页面级存储,GetShared接口仅能获取当前Stage通过windowStage.loadContent传入的LocalStorage实例,否则返回undefined |
-
当@LocalStorageLink(key)装饰的数值改变被观察到时,修改将被同步回LocalStorage对应属性键值key的属性中。
-
LocalStorage中属性键值key对应的数据一旦改变,属性键值key绑定的所有的数据(包括双向@LocalStorageLink和单向@LocalStorageProp)都将同步修改;
-
当@LocalStorageLink(key)装饰的数据本身是状态变量,它的改变不仅仅会同步回LocalStorage中,还会引起所属的自定义组件的重新渲染。
🍬1.2.2.1.2 使用场景
1、应用逻辑使用LocalStorage
2、从UI内部使用LocalStorage
3、@LocalStorageProp和LocalStorage单向同步的简单场景
4、@LocalStorageLink和LocalStorage双向同步的简单场景
5、兄弟节点之间同步状态变量
6、将LocalStorage实例从UIAbility共享到一个或多个视图
🌈1.2.2.2 AppStorage:AppStorage
🍬1.2.2.2.1 变化规则
和前面一样传递的参数变成@StorageProp和@StorageLink
- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
- 当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性。
- 当装饰的对象是array时,可以观察到数组添加、删除、更新数组单元的变化。
🍬1.2.2.2.2 使用场景
1、从应用逻辑使用AppStorage和LocalStorage
2、从UI内部使用AppStorage和LocalStorage
3、不建议借助@StorageLink的双向同步机制实现事件通知
以上通知事件逻辑简化成三元表达式
AppStorage与PersistentStorage以及Environment配合使用时,需要注意以下几点:
-
在AppStorage中创建属性后,调用PersistentStorage.persistProp()接口时,会使用在AppStorage中已经存在的值,并覆盖PersistentStorage中的同名属性,所以建议要使用相反的调用顺序,反例可见在PersistentStorage之前访问AppStorage中的属性;
-
如果在AppStorage中已经创建属性后,再调用Environment.envProp()创建同名的属性,会调用失败。因为AppStorage已经有同名属性,Environment环境变量不会再写入AppStorage中,所以建议AppStorage中属性不要使用Environment预置环境变量名。
-
状态装饰器装饰的变量,改变会引起UI的渲染更新,如果改变的变量不是用于UI更新,只是用于消息传递,推荐使用 emitter方式。例子可见不建议借助@StorageLink的双向同步机制实现事件通知。
🌈1.2.2.3 PersistentStorage:持久化存储UI状态
概述 | 解释 |
---|---|
持久化 | PersistentStorage会将选定的AppStorage属性保存在设备磁盘上。应用程序可以使用API来决定哪些AppStorage属性应该被持久化。UI和业务逻辑不直接访问PersistentStorage中的属性,所有属性访问都是对AppStorage的访问,而且AppStorage中的更改会自动同步到PersistentStorage。 |
属性同步 | PersistentStorage和AppStorage中的属性建立双向同步。应用程序开发人员通常通过AppStorage访问PersistentStorage,还有一些接口可用于管理持久化属性,但业务逻辑始终通过AppStorage获取和设置属性。 |
限制条件 | PersistentStorage允许的类型和值包括数字、字符串、布尔值、枚举等简单类型以及可以通过JSON.stringify()和JSON.parse()重建的对象。不支持的类型和值包括嵌套对象(如对象数组、对象属性为对象等)、undefined和null。 |
性能 | 应避免持久化大型数据集和经常变化的变量,因为持久化数据是一个相对缓慢的操作。最好使用小于2kb的数据进行持久化,如果需要存储大量的数据,建议使用数据库API,因为PersistentStorage写入磁盘的操作是同步的,大量的数据本地化读写会同步在UI线程中执行,影响UI渲染性能。 |
使用限制 | PersistentStorage只能在UI页面内使用,否则将无法持久化数据。 |
🍬1.2.2.3.1 变化规则
类似AppStorage,流程图如下:
🍬1.2.2.3.2 使用场景
🌈1.2.2.4 Environment:设备环境查询
Environment是ArkUI框架在应用程序启动时创建的单例对象。它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的(即应用不可写入),所有的属性都是简单类型。
🍬1.2.2.4.1 变化规则
不可读写
🍬1.2.2.4.2 使用场景
1、从UI中访问Environment参数
2、应用逻辑使用Environment
☀️1.2.3 其他状态管理功能
- @Watch:用于监听状态变量的变化。
**$$**运算符:给内置组件提供TS变量的引用,使得TS变量和内置组件的内部状态保持同步。
🌈1.2.2.1 使用场景
1、@Watch和自定义组件更新
2、@Watch与@Link组合使用
🌈1.2.2.2 *$$*语法:内置组件双向同步
