关于harmonyOS Next中状态管理的学习
前段时间复习了下鸿蒙的状态管理,这里分享下学习的内容
概述:
状态:指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。
一、组件状态管理
1、@State
@State Name: string 可以使变量成为状态变量,对于简单类型(boolean、string、number)和复杂类型(class、object)的非嵌套属性的修改都可以观察到
2、@Prop
@Prop Name: string 可以使用在子组件中,使得父组件能够使用它,是单向数据同步 如: son.ets struct Count{ @Prop count: number = 0; } father.ets struct father{ Count({ count = 3; }) } 注:@Prop 创建变量时,可以不进行赋值,但如果这样做了,在父组件调用时就必须要赋值
3、@Link
@Link name: string 可以使用在子组件中,使得父组件能够使用和修改它,是双向数据同步
4、@Provide / @Consume
说明: @Provide装饰的状态变量自动对其所有后代组件可用, 后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步 示范: // 通过相同的变量名绑定 @Provide a: number = 0; @Consume a: number; //通过相同的别名绑定 @Provide('a') b: number = 0; @Consume('a') c: number;
5、@Observed / @ObjectLink
说明: @Observed只能用于装饰class @ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop 示范: @Observed class Parent { public child: Child; public count: number; constructor(child: Child, count: number) { this.child = child; this.count = count; } } @ObjectLink parent: Parent // 赋值变化可以被观察到 this.parent.child = new Child(5) this.parent.count = 5
二、应用状态管理
1、LocalStorage:页面级UI状态存储
@LocalStorageProp / @LocalStorageLink
说明:
应用程序可以创建多个LocalStorage实例,LocalStorage实例可以在页面内共享,
也可以通过GetShared接口,实现跨页面、UIAbility实例内共享。
示例:
1、页面内的使用
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); // 创建新实例并使用给定对象初始化
let propA: number | undefined = storage.get('PropA') // propA == 47
let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);
storage.setOrCreate('PropB', new PropB(50));
@Component
struct Child{
// @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
@LocalStorageLink('PropA') childLinkNumber: number = 1;
// @LocalStorageLink变量装饰器与LocalStorage中的'PropB'属性建立双向绑定
@LocalStorageLink('PropB') childLinkObject: PropB = new PropB(0);
}
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
...
2、在多页面中的使用
说明:
上面的实例中,LocalStorage的实例仅仅在一个@Entry装饰的组件和其所属的子组件(一个页面)中共享
如果希望其在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,
并调用windowStage.loadContent。
// EntryAbility.ets
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
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);
}
}
// index.ets
import { router } from '@kit.ArkUI';
// 通过getShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.getShared()
@Entry(storage)
@Component
struct Index {
// can access LocalStorage instance using
// @LocalStorageLink/Prop decorated variables
@LocalStorageLink('PropA') propA: number = 1;
build() {
Row() {
Column() {
Text(`${this.propA}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button("To Page")
.onClick(() => {
this.getUIContext().getRouter().pushUrl({
url: 'pages/Page'
})
})
}
.width('100%')
}
.height('100%')
}
}
// Page.ets
import { router } from '@kit.ArkUI';
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(() => {
this.getUIContext().getRouter().back()
})
}
.width('100%')
}
}
}
复制复制
2、AppStorage:应用全局的UI状态存储
@StorageLink
说明:
本地修改发生,该修改会被写回AppStorage中;
AppStorage中的修改发生后,该修改会被同步到所有绑定AppStorage对应key的属性上,
包括单向(@StorageProp和通过Prop创建的单向绑定变量)、
双向(@StorageLink和通过Link创建的双向绑定变量)变量和其他实例(比如PersistentStorage)
示例:
Index.ets
import router from '@ohos.router';
AppStorage.setOrCreate('PropA', 47);
@Entry
@Component
struct Index {
@StorageLink('PropA') storageLink: number = 1
build() {
Column(){
Text(`${this.storageLink}`)
Button('+1').onClick(() => {
this.storageLink += 1;
})
Button('page').onClick((event: ClickEvent) => {
router.pushUrl({
"url": "pages/Page"
})
})
}
}
}
Page.ets
import router from '@ohos.router';
@Entry
@Component
struct Index {
@StorageLink('PropA') storageLink: number = 2
build() {
Column(){
Text(`${this.storageLink}`)
Button('+1').onClick(() => {
this.storageLink += 1;
})
Button('page').onClick((event: ClickEvent) => {
router.back()
})
}
}
}
复制复制
3、PersistentStorage:持久化存储UI状态
概述:
PersistentStorage将选定的AppStorage属性保留在设备磁盘上,
可以实现应用推出再次启动后,依然能保存选定的结果
PersistentStorage和AppStorage中的属性建立双向同步。
应用开发通常通过AppStorage访问PersistentStorage,
另外还有一些接口可以用于管理持久化属性,但是业务逻辑始终是通过AppStorage获取和设置属性的。
使用步骤:
1、初始化PersistentStorage:
PersistentStorage.persistProp('aProp', 47);
2、在AppStoreage中获取对应属性:
AppStorage.get<number>('aProp'); // returns 47
// 除此之外,也可以在组件中直接定义
@StorageLink('aProp') aProp: number = 48
3、完整代码示例:
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;
})
}
}
}
}
复制复制
三、其他状态管理
1、@Watch:状态更改通知
@Watch('更新时运行函数') 变量名: string = ''
示例:
@Link @Watch('onCountUpdated') count: number = 0;
@State total: number = 0;
onCountUpdated(propName: string): void {
this.total += this.count;
}
复制复制
2、$$:内置组件双向同步
参考以下案例:
// xxx.ets
@Entry
@Component
struct TextInputExample {
@State text: string = ''
controller: TextInputController = new TextInputController()
build() {
Column({ space: 20 }) {
Text(this.text)
TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller })
.placeholderColor(Color.Grey)
.placeholderFont({ size: 14, weight: 400 })
.caretColor(Color.Blue)
.width(300)
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
当对TextInput进行输入时,可以自动修改this.text本身的值
复制复制
3、@Track:class对象属性级更新
说明:
@Track是class对象的属性装饰器。当一个class对象是状态变量时,
@Track装饰的属性发生变化,只会触发该属性关联的UI更新;
而未被标记的属性不能在UI中使用,如果使用,会发生运行时报错。
使用@Track装饰器,可以避免冗余刷新
参照以下代码:
class LogTrack {
@Track str1: string;
@Track str2: string;
constructor(str1: string) {
this.str1 = str1;
this.str2 = 'World';
}
}
class LogNotTrack {
str1: string;
str2: string;
constructor(str1: string) {
this.str1 = str1;
this.str2 = '世界';
}
}
@Entry
@Component
struct AddLog {
@State logTrack: LogTrack = new LogTrack('Hello');
@State logNotTrack: LogNotTrack = new LogNotTrack('你好');
isRender(index: number) {
console.log(`Text ${index} is rendered`);
return 50;
}
build() {
Row() {
Column() {
Text(this.logTrack.str1) // UINode1
.fontSize(this.isRender(1))
.fontWeight(FontWeight.Bold)
Text(this.logTrack.str2) // UINode2
.fontSize(this.isRender(2))
.fontWeight(FontWeight.Bold)
Button('change logTrack.str1')
.onClick(() => {
this.logTrack.str1 = 'Bye';
})
Text(this.logNotTrack.str1) // UINode3
.fontSize(this.isRender(3))
.fontWeight(FontWeight.Bold)
Text(this.logNotTrack.str2) // UINode4
.fontSize(this.isRender(4))
.fontWeight(FontWeight.Bold)
Button('change logNotTrack.str1')
.onClick(() => {
this.logNotTrack.str1 = '再见';
})
}
.width('100%')
}
.height('100%')
}
}
在上面的示例中:
类LogTrack中的属性均被@Track装饰器装饰,点击按钮"change logTrack.str1",此时UINode1刷新,UINode2不刷新,只有一条日志输出,避免了冗余刷新。
Text 1 is rendered
类logNotTrack中的属性均未被@Track装饰器装饰,点击按钮"change logNotTrack.str1",此时UINode3、UINode4均会刷新,有两条日志输出,存在冗余刷新。
Text 3 is rendered
Text 4 is rendered
复制复制
4、freezeWhenInactive:自定义组件冻结
说明:
当一个状态变量绑定了多个UI组件时,其变化可能触发大量UI组件的刷新,
使用组件冻结功能,可以使目前未用到的组件不进行刷新,可以提高UI界面的刷新性能
注:
组件冻结目前仅适用于以下场景:
页面路由:当前栈顶页面为active,非栈顶不可见页面为inactive。
TabContent:只有当前显示的TabContent中的自定义组件处于active状态,其余则为inactive。
LazyForEach:仅当前显示的LazyForEach中的自定义组件为active,而缓存节点的组件则为inactive。
Navigation:当前显示的NavDestination中的自定义组件为active,而其他未显示的NavDestination组件则为inactive。
用法:
@Component({ freezeWhenInactive: true })