如何接做网站编程的生意,爱站网反链查询,关键词排名优化公司哪家强,成都网站建设选择到访率[写在前面: 文章多处用到gif动图#xff0c;如未自动播放#xff0c;请点击图片] 衔接上一篇:鸿蒙开发-ArkTS 语言-基础语法
3. 状态管理
变量必须被装饰器装饰才能成为状态变量#xff0c;状态变量的改变才能导致 UI 界面重新渲染
概念描述状态变量被状态装饰器装饰的变…[写在前面: 文章多处用到gif动图如未自动播放请点击图片] 衔接上一篇:鸿蒙开发-ArkTS 语言-基础语法
3. 状态管理
变量必须被装饰器装饰才能成为状态变量状态变量的改变才能导致 UI 界面重新渲染
概念描述状态变量被状态装饰器装饰的变量改变会引起UI的渲染更新。常规变量没有状态的变量通常应用于辅助计算。它的改变永远不会引起UI的刷新。数据源/同步源状态变量的原始来源可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。命名参数机制父组件通过指定参数传递给子组件的状态变量为父子传递同步参数的主要手段。示例CompA: ({ aProp: this.aProp }。从父组件初始化父组件使用命名参数机制将指定参数传递给子组件。本地初始化的默认值在有父组件传值的情况下会被覆盖。
Components部分的装饰器为组件级别的状态管理Application部分为应用的状态管理。可以通StorageLink/LocalStorageLink和StorageProp/LocalStorageProp实现应用和组件状态的双向和单向同步。图中箭头方向为数据同步方向单箭头为单向同步双箭头为双向同步。
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-state-management-overview-0000001524537145-V2 状态管理分三种 组件级别即components级别 StateState装饰的变量拥有其所属组件的状态可以作为其子组件单向和双向同步的数据源。当其数值改变时会引起相关组件的渲染刷新。PropProp装饰的变量可以和父组件建立单向同步关系Prop装饰的变量是可变的但修改不会同步回父组件。LinkLink装饰的变量和父组件构建双向同步关系的状态变量父组件会接受来自Link装饰的变量的修改的同步父组件的更新也会同步给Link装饰的变量。Provide/ConsumeProvide/Consume装饰的变量用于跨组件层级多层组件同步状态变量可以不需要通过参数命名机制传递通过alias别名或者属性名绑定。ObservedObserved装饰class需要观察多层嵌套场景的class需要被Observed装饰。单独使用Observed没有任何作用需要和ObjectLink、Prop连用。ObjectLinkObjectLink装饰的变量接收Observed装饰的class的实例应用于观察多层嵌套场景和父组件的数据源构建双向同步。 应用级别即 Application 级别 AppStorage是应用程序中的一个特殊的单例LocalStorage对象是应用级的数据库和进程绑定通过StorageProp和StorageLink装饰器可以和组件联动。AppStorage是应用状态的“中枢”需要和组件UI交互的数据存入AppStorage比如持久化数据PersistentStorage和环境变量Environment。UI再通过AppStorage提供的装饰器或者API接口访问这些数据框架还提供了LocalStorageAppStorage是LocalStorage特殊的单例。LocalStorage是应用程序声明的应用状态的内存“数据库”通常用于页面级的状态共享通过LocalStorageProp和LocalStorageLink装饰器可以和UI联动。 其他状态管理功能 Watch用于监听状态变量的变化。 $$ 运算符给内置组件提供TS变量的引用使得TS变量和内置组件的内部状态保持同步。
3.1 组件级别的状态管理
3.1.1 State 组件内状态
State装饰的变量是私有的只能从组件内部访问在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。
示例如下
Entry
Component
struct Index {build() {Column() {Parent()}}
}Component
struct MyComponent {State count: number 0;private increaseBy: number 1;build() {Column(){Row() {Text(${this.count}).fontSize(108)}Row() {Button(click me hh).onClick(() {this.count this.increaseBy})}}}
}Component
struct Parent {build() {Column() {// 从父组件初始化覆盖本地定义的默认值MyComponent({ count: 2, increaseBy: 2 })}}
}效果如下 3.1.2 能观察的数据类型
观察简单类型的变化 当状态变量是boolean、string、number类型时修改这些变量的数值可以被观察到从而引起UI的刷新。
State count: number 0;
this.count 1; // 这种修改可以被观察到观察类和对象类型的变化 对于class或Object类型可以观察到自身的赋值变化以及其属性的赋值变化。但是嵌套属性的赋值观察不到。
class ClassA {public value: string;constructor(value: string) {this.value value;}
}class Model {public value: string;public name: ClassA;constructor(value: string, a: ClassA) {this.value value;this.name a;}
}State title: Model new Model(Hello, new ClassA(World));// class类型赋值可以观察到
this.title new Model(Hi, new ClassA(ArkUI));// class属性的赋值可以观察到
this.title.value Hi;// 嵌套属性的赋值观察不到
this.title.name.value ArkUI;观察数组类型的变化 当状态变量是数组时可以观察到数组本身的赋值以及对数组的添加、删除、更新操作。
class Model {public value: number;constructor(value: number) {this.value value;}
}State title: Model[] [new Model(11), new Model(1)];// 数组自身的赋值可以观察到
this.title [new Model(2)];// 数组项的赋值可以观察到
this.title[0] new Model(2);// 删除数组项可以观察到
this.title.pop();// 新增数组项可以观察到
this.title.push(new Model(12));3.1.2 Prop装饰器父子单向通信
可以使用 Prop 定义要从父级接受的变量。注意以下两点 Prop变量可以在子组件内修改但修改后的变化不会同步回父组件中。 但当父组件中的数据源更改时与之相关的Prop装饰的变量都会自动更新。如果子组件已经在本地修改了Prop装饰的相关变量值而在父组件中对应的State装饰的变量被修改后子组件本地修改的Prop装饰的相关变量值将被覆盖。 允许装饰的变量的类型string、number、boolean、enum类型。 不允许的类型anyundefined和null。
Prop 初始化规则
图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-prop-0000001473537702-V2 代码示例
Component
struct CountDownComponent {Prop count: number;costOfOneAttempt: number 1;build() {Column() {if (this.count 0) {Text(儿子还有 ${this.count} 个萝卜头)} else {Text(吃完了要挨打了)}// Prop装饰的变量不会同步给父组件Button(儿子偷吃了${this.costOfOneAttempt}个萝卜头).onClick(() {this.count - this.costOfOneAttempt;})}}
}Entry
Component
struct ParentComponent {State countDownStartValue: number 10;build() {Column() {Text(老爸还有 ${this.countDownStartValue} 个萝卜头)// 父组件的数据源的修改会同步给子组件Button(一起买了1个萝卜头).onClick(() {this.countDownStartValue 1;})// 父组件的修改会同步给子组件Button(一起吃了1个萝卜头).onClick(() {this.countDownStartValue - 1;})CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })}}
}
效果如下 3.1.3 Link: 父子双向通信
可以使用 Link 装饰器进行数据双向同步
Link 是双向同步的装饰器父组件中State, StorageLink和Link 和子组件Link可以建立双向数据同步反过来也是可以的。允许装饰的类型有Object、class、string、number、boolean、enum类型同时类型必须被指定且和双向绑定状态变量的类型相同。不支持any不支持简单类型和复杂类型的联合类型不允许使用undefined和null。当装饰的数据类型为boolean、string、number时可以同步观察到数值的变化为class或Object时可以观察到赋值和属性赋值的变化为array时可以观察到数组添加、删除、更新数组单元的变化。
Link初识化规则
图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-link-0000001524297305-V2 代码示例
class GreenButtonState {width: number 0;constructor(width: number) {this.width width;}
}// class类型的子组件GreenButton通过Link装饰变量greenButtonState同步到父组件Index中的State变量greenButtonStateComponent
struct GreenButton {Link greenButtonState: GreenButtonState;build() {Button(绿色按钮).width(this.greenButtonState.width).height(150.0).fontWeight(FontWeight.Bold).backgroundColor(#ff81c123).onClick(() {if (this.greenButtonState.width 700) {// 更新class的属性变化可以被观察到同步回父组件this.greenButtonState.width 125;} else {// 更新class变化可以被观察到同步回父组件this.greenButtonState new GreenButtonState(100);}})}
}// 简单类型的子组件YellowButton通过Link装饰变量yellowButtonState同步到父组件Index中的State变量yellowButtonProp
Component
struct YellowButton {Link yellowButtonState: number;build() {Button(黄色按钮).width(this.yellowButtonState).height(150.0).fontWeight(FontWeight.Bold).backgroundColor(#ffdb944d).onClick(() {// 子组件的简单类型可以同步回父组件this.yellowButtonState 50.0;})}
}// 父组件Index初始化并管理子组件的状态变量
Entry
Component
struct Index {State greenButtonState: GreenButtonState new GreenButtonState(300);State yellowButtonProp: number 100;build() {Column() {// 按钮父组件修改greenButtonState同步到GreenButton子组件Button(父视图: 设置 绿色按钮).onClick(() {this.greenButtonState.width (this.greenButtonState.width 700) ? this.greenButtonState.width 100 : 100;})// 按钮父组件修改yellowButtonProp同步到YellowButton子组件Button(父视图: 设置 黄色按钮).onClick(() {this.yellowButtonProp (this.yellowButtonProp 700) ? this.yellowButtonProp 100 : 100;})// 子组件GreenButton通过Link同步更新父组件State的greenButtonStateGreenButton({ greenButtonState: $greenButtonState })// 子组件YellowButton通过Link同步更新父组件State的yellowButtonPropYellowButton({ yellowButtonState: $yellowButtonProp })}}
}
图示 3.1.4 Provide/Consume 装饰器
为了避免在组件中多次传递变量推出了一种使某些变量能被所有后代组件使用的装饰器 Provide后代组件可以使用 Consume去获取 Provide 的值而State和Link 只能在父子组件中传递。
// 通过相同的变量名绑定
Provide a: number 0;
Consume a: number;// 通过相同的变量别名绑定
Provide(a) b: number 0;
Consume(a) c: number;Provide 和 Consume 是双向同步的。
代码示例
Component
struct CompD {// Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的Provide装饰的变量Consume tickets: number;build() {Column() {Text(评审投票数(${this.tickets}))Button(评审投票数(${this.tickets})1).onClick(() this.tickets 1)}.width(50%)}
}Component
struct CompC {build() {Row({ space: 5 }) {CompD()CompD()}}
}Component
struct CompB {build() {CompC()}
}Entry
Component
struct Index {// Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件Provide tickets: number 0;build() {Column() {Button(评审投票数(${this.tickets})1).onClick(() this.tickets 1)CompB()}}
}
图示 3.1.5 Observed 装饰器和ObjectLink装饰器
对于多层嵌套的情况比如二维数组或者数组项class或者class的属性是class他们的第二层的属性变化是无法观察到的。这就引出了Observed/ObjectLink装饰器。
ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步
被Observed装饰的类可以被观察到属性的变化子组件中ObjectLink装饰器装饰的状态变量用于接收Observed装饰的类的实例和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被Observed装饰的项或者是class object中的属性这个属性同样也需要被Observed装饰。单独使用Observed是没有任何作用的需要搭配ObjectLink或者Prop使用。不要用Observed和其他类装饰器装饰同一个classObserved会改变class的原型链
初始化图示
图片来源https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-observed-and-objectlink-0000001473697338-V2#section2976114355019 以二维数组监听为例
Observed
class StringArray extends ArrayString {
}Component
struct ItemPage {ObjectLink itemArr: StringArray;build() {Row() {Text(ItemPage).width(100).height(100)ForEach(this.itemArr,item {Text(item).width(100).height(100)},item item)}}
}Entry
Component
struct Index {State arr: ArrayStringArray [new StringArray(), new StringArray(), new StringArray()];build() {Column() {ItemPage({ itemArr: this.arr[0] })ItemPage({ itemArr: this.arr[1] })ItemPage({ itemArr: this.arr[2] })Divider()ForEach(this.arr,itemArr {ItemPage({ itemArr: itemArr })},itemArr itemArr[0])Divider()Button(update).onClick(() {console.error(Update all items in arr);if (this.arr[0][0] ! undefined) {// 正常情况下需要有一个真实的ID来与ForEach一起使用但此处没有// 因此需要确保推送的字符串是唯一的。this.arr[0].push(${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()});this.arr[1].push(${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()});this.arr[2].push(${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()});} else {this.arr[0].push(Hello);this.arr[1].push(World);this.arr[2].push(!);}})}}
}效果如下 3.2 App级别的状态管理
ArkTS可以实现多种应用状态管理的能力具体表现在以下几点
LocalStorage: 是页面级的状态储存通常用于 UIAblility内也可以用于页面间状态共享AppStorage: 特殊的单例 LocalStorage 对象为程序的 UI 状态属性提供中央储存PersistentStorage: 持久化 UI 状态储存通常于 AppStorage 一起使用可以把 AppStorage 存储的数据写入磁盘确保数据在重启前后保持一致Environment: 应用程序运行的设备环境参数会同步到 AppStorage 中可以和AppStorage 搭配使用
3.2.1 LocalStorage: 页面级 UI 状态存储
有关 LocalStorage 介绍
一个应用程序可以创建多个 LocalStorage 实例这些实例可以在页面内共享也可以通过 GetShared 接口在 UIAbility 中创建的实例实现跨页面和 UIAbility 内的共享。根据组件树的结构被 Entry 装饰的 Component 实例可以被分配一个 LocalStorage 实例而该组件的所有子组件实例将自动获得对该 LocalStorage 实例的访问权限。被 Component 装饰的组件最多可以访问一个 LocalStorage 实例和 AppStorage而未被 Entry 装饰的组件无法独立分配 LocalStorage 实例只能接受父组件通过 Entry 传递来的 LocalStorage 实例。一个 LocalStorage 实例在组件树上可以被分配给多个组件而其中的所有属性都是可变的。当应用释放最后一个指向LocalStorage的引用时比如销毁最后一个自定义组件LocalStorage将被JS Engine垃圾回收。被绑定的属性值变化无论父子级关系如何都会引起依赖此变量的 UI 刷新渲染。
装饰器
LocalStorageProp: LocalStorageProp(key) 是一个装饰器用于在 ArkUI 组件框架中建立自定义组件的属性与 LocalStorage 中特定键对应属性之间的单向数据同步。 初始化与绑定 在自定义组件初始化时被 LocalStorageProp(key) 装饰的变量通过给定的 key 绑定到相应的 LocalStorage 属性上完成初始化。本地初始化是必要的因为不能保证在组件初始化之前 LocalStorage 中是否存在给定的 key。 本地修改和同步 对于使用 LocalStorageProp(key) 装饰的变量本地的修改是允许的。但是需要注意本地的修改永远不会同步回 LocalStorage 中。相反如果 LocalStorage 中给定 key 的属性发生改变这个改变会被同步给被 LocalStorageProp(key) 装饰的变量并覆盖本地的修改。 LocalStorageLinkLocalStorageLink 是一个装饰器用于在 ArkUI 组件框架中建立自定义组件的状态变量与 LocalStorage 中特定键对应属性之间的双向数据同步。以下是相关信息的概述 同步规则 LocalStorageLink(key) 与 LocalStorage 中给定 key 对应的属性建立双向数据同步。这包括本地的修改会同步回 LocalStorage 中以及 LocalStorage 中的修改会被同步到所有绑定了相同 key 的属性上。初始化规则LocalStorageLink 不支持从父节点初始化只能从 LocalStorage 中的 key 对应的属性初始化。如果没有对应的 key则使用本地默认值初始化。可用于初始化 State、Link、Prop、Provide但不支持组件外访问
语法示例
let storage new LocalStorage({ KeyA: 47 }); // 创建新实例并使用给定对象初始化
let keyA storage.get(KeyA); // keyA 47let link1 storage.link(KeyA); // link1.get() 47
let link2 storage.link(KeyA); // link2.get() 47let prop storage.prop(KeyA); // prop.get() 47link1.set(48); // 双向绑定 link1.get() link2.get() prop.get() 48prop.set(1); // 单向绑定 prop.get() 1但是 link1.get() link2.get() 48link1.set(49); // 双向绑定 link1.get() link2.get() prop.get() 49
案例理解
// 创建新实例并使用给定对象初始化
let localStorageInstance new LocalStorage({ PropertyA: 47 });Component
struct ChildComponent {// 与LocalStorage中的PropertyA属性建立双向绑定LocalStorageLink(PropertyA) linkedProperty2: number 1;// 与LocalStorage中的PropertyA属性建立单向绑定LocalStorageProp(PropertyA) linkedProperty3: number 1;build() {Row() {Button(子组件的值 ${this.linkedProperty2})// 更改将同步至LocalStorage中的PropertyA以及ParentComponent.linkedProperty1.onClick(() this.linkedProperty2 1)// Local 中值变化linkedProperty3 也会变反之不会Button(子组件的值-单向 Prop ${this.linkedProperty3}).onClick(() this.linkedProperty3 1)}}
}// 使LocalStorage可被Component组件访问
Entry(localStorageInstance)
Component
struct ParentComponent {// LocalStorageLink变量装饰器与LocalStorage中的PropertyA属性建立双向绑定LocalStorageLink(PropertyA) linkedProperty1: number 1;build() {Column({ space: 15 }) {Button(父组件的值 ${this.linkedProperty1}) // 初始化值从LocalStorage中获取因为PropertyA已经初始化为47.onClick(() this.linkedProperty1 1)// Component子组件自动获得对ParentComponent LocalStorage实例的访问权限。ChildComponent()}}
}效果如下 3.2.2 AppStorage应用全局的UI状态存储
概述
AppStorage是应用全局的UI状态存储是和应用的进程绑定的由UI框架在应用程序启动时创建为应用程序UI状态属性提供中央存储。AppStorage是应用级的全局状态共享相当于整个应用的“中枢”持久化数据PersistentStorage和 环境变量Environment都是通过AppStorage的中转才可以和UI交互。AppStorage中的属性可以被双向同步数据可以是存在于本地或远程设备上并具有不同的功能比如数据持久化。
基本用法
// 在AppStorage中设置或创建 PropA 属性初始值为 47
AppStorage.SetOrCreate(PropA, 47);let localStorageInstance: LocalStorage new LocalStorage({ PropA: 17 });// 从AppStorage中获取 PropA 的值此时 propA 在 AppStorage 中为 47在 LocalStorage 中为 17
let propAFromAppStorage: number AppStorage.Get(PropA);// 使用AppStorage的Link方法创建两个链接link1和link2以同步 PropA 的值
let link1: SubscribedAbstractPropertynumber AppStorage.Link(PropA);
let link2: SubscribedAbstractPropertynumber AppStorage.Link(PropA);// 使用AppStorage的Prop方法创建一个属性prop以单向同步 PropA 的值
let prop: SubscribedAbstractPropertynumber AppStorage.Prop(PropA);// 修改 link1演示双向绑定的效果所有其他绑定到相同键的变量都同步更新
link1.set(48); // link1.get() link2.get() prop.get() 48// 修改属性 prop演示单向绑定的效果只有属性本身更新其他变量不受影响
prop.set(1); // prop.get() 1但是 link1.get() link2.get() 48// 再次修改 link1验证双向绑定所有绑定到相同键的变量都同步更新
link1.set(49); // link1.get() link2.get() prop.get() 49// 使用 PropA 的值从 LocalStorage 中获取此时为 17
let valueFromLocalStorage: number localStorageInstance.get(PropA);// 设置 PropA 的值为 101
localStorageInstance.set(PropA, 101);// 从 LocalStorage 中获取 PropA 的值此时为 101
let valueFromLocalStorageAfterSet: number localStorageInstance.get(PropA);// 从 AppStorage 中获取 PropA 的值此时为 49
let valueFromAppStorage: number AppStorage.Get(PropA);// 获取 link1 的值此时为 49
let valueFromLink1: number link1.get();// 获取 link2 的值此时为 49
let valueFromLink2: number link2.get();// 获取 prop 的值此时为 49
let valueFromProp: number prop.get();
3.2.3 PersistentStorage持久化存储UI状态
LocalStorage和AppStorage都是运行时的内存但是在应用退出再次启动后依然能保存选定的结果是应用开发中十分常见的现象这就需要用到PersistentStorage。
注意
持久化数据操作相对较慢应避免持久化大型数据集和经常变化的变量。PersistentStorage的持久化变量最好是小于2kb的数据以避免影响UI渲染性能。PersistentStorage只能在UI页面内使用否则无法持久化数据。不在调用 PersistentStorage 前调用 AppStorage会导致上次退出应用保存的值丢失。
代码示例
PersistentStorage.PersistProp(userScore, 100);Entry
Component
struct Game {State message: string Welcome to the GameStorageLink(userScore) userScore: number 50build() {Row() {Column() {Text(this.message)Text(你的得分: ${this.userScore}).onClick(() {this.userScore 10;})}}}
}3.2.4 Environment设备环境查询
Environment设备环境查询用于查询设备运行环境参数是ArkUI框架在应用程序启动时创建的单例对象它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的即应用不可写入所有的属性都是简单类型。
使用场景
Environment.EnvProp将设备的语言设置为英语 zh。然后StorageProp将设备语言与AppStorage中的 deviceLanguage 建立了单向同步。
// 将设备的语言code存入AppStorage
Environment.EnvProp(deviceLanguage, zh);Entry
Componentstruct Main {// 使用StorageProp链接到Component中StorageProp(deviceLanguage) selectedLanguage: string en;build() {Row() {Column() {Text(Device Language:)Text(this.selectedLanguage)}}}
}// 应用逻辑使用Environment
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractPropertystring AppStorage.Prop(deviceLanguage);
if (lang.get() zh) {console.info(你好);
} else {console.info(Hello!);
}3.3 其他状态管理
3.3.1 Watch 监听
Watch用于监听状态变量的变化当状态变量变化时Watch的回调方法将被调用。Watch在ArkUI框架内部判断数值有无更新使用的是严格相等遵循严格相等规范。当在严格相等为false的情况下就会触发Watch的回调。
注意
当观察到状态变量的变化包括双向绑定的AppStorage和LocalStorage中对应的key发生的变化的时候对应的Watch的回调方法将被触发如果在Watch的方法里改变了其他的状态变量也会引起状态变更和Watch的执行要避免无限循环在第一次初始化的时候Watch装饰的方法不会被调用即认为初始化不是状态变量的改变。
示例
Component
struct TotalView {Prop Watch(onCountUpdated) count: number;State total: number 0;// Watch cbonCountUpdated(propName: string): void {this.total this.count;}build() {Text(Total: ${this.total})}
}Entry
Component
struct CountModifier {State count: number 0;build() {Column() {Button(add to basket).onClick(() {this.count})TotalView({ count: this.count })}}
}3.3.2 $$语法内置组件双向同步
$$运算符为系统内置组件提供TS变量的引用使得TS变量和系统内置组件的内部状态保持同步。
内部状态具体指什么取决于组件。
当前$$支持基础类型变量以及State、Link和Prop装饰的变量。当前$$仅支持 bindPopup 属性方法的show参数Radio组件的checked属性Refresh组件的refreshing参数。$$绑定的变量变化时会触发UI的同步刷新。
Entry
Component
struct bindPopupPage {State customPopup: boolean false;build() {Column() {Button(Popup).margin(20).onClick(() {this.customPopup !this.customPopup}).bindPopup($$this.customPopup, {message: showPopup})}}
}衔接下一篇: 鸿蒙开发-ArkTS 语言-循环渲染