HarmonyOS鸿蒙Next中关于状态管理的学习

HarmonyOS鸿蒙Next中关于状态管理的学习 前段时间复习了下鸿蒙的状态管理,这里分享下学习的内容

概述:

状态:指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。

cke_1280.png

一、组件状态管理

1、@State

@State Name: string 可以使变量成为状态变量,对于简单类型(boolean、string、number)和复杂类型(class、object)的非嵌套属性的修改都可以观察到

2、@Prop

@Prop Name: string 可以使用在子组件中,使得父组件能够使用它,是单向数据同步 如: son.ets

struct Count{
    [@Prop](/user/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](/user/Provide) a: number = 0;
[@Consume](/user/Consume) a: number;

//通过相同的别名绑定
[@Provide](/user/Provide)('a') b: number = 0;
[@Consume](/user/Consume)('a') c: number;
5、@Observed / @ObjectLink

说明: @Observed只能用于装饰class @ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop

示范:

[@Observed](/user/Observed) class Parent {
    public child: Child;
    public count: number;
    constructor(child: Child, count: number) {
        this.child = child;
        this.count = count;
    }
}
[@ObjectLink](/user/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](/user/LocalStorageLink)变量装饰器与LocalStorage中的'PropA'属性建立双向绑定       
    [@LocalStorageLink](/user/LocalStorageLink)('PropA') childLinkNumber: number = 1;

    // [@LocalStorageLink](/user/LocalStorageLink)变量装饰器与LocalStorage中的'PropB'属性建立双向绑定
    [@LocalStorageLink](/user/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](/user/LocalStorageLink)/Prop decorated variables
  [@LocalStorageLink](/user/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](/user/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](/user/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](/user/Watch)('更新时运行函数') 变量名: string = ''

示例:

[@Link](/user/Link) [@Watch](/user/Watch)('onCountUpdated') count: number = 0;
[@State](/user/State) total: number = 0;

onCountUpdated(propName: string): void {
  this.total += this.count;
}

2、$$:内置组件双向同步

参考以下案例:

// xxx.ets
@Entry
@Component
struct TextInputExample {
  [@State](/user/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](/user/Track) str1: string;
  [@Track](/user/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](/user/State) logTrack: LogTrack = new LogTrack('Hello');
  [@State](/user/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 })

更多关于HarmonyOS鸿蒙Next中关于状态管理的学习的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

在HarmonyOS鸿蒙Next中,状态管理主要依赖于ArkUI框架提供的状态管理机制。ArkUI框架通过[@State](/user/State)[@Prop](/user/Prop)[@Link](/user/Link)[@Observed](/user/Observed)[@ObjectLink](/user/ObjectLink)等装饰器来实现组件状态的管理和传递。

  1. @State:用于声明组件的内部状态。当[@State](/user/State)修饰的变量发生变化时,组件会自动重新渲染。[@State](/user/State)通常用于管理组件的私有状态。

  2. @Prop:用于从父组件向子组件传递数据。[@Prop](/user/Prop)修饰的变量是只读的,子组件不能直接修改它。当父组件的状态发生变化时,子组件会自动更新。

  3. @Link:用于在父子组件之间建立双向绑定。[@Link](/user/Link)修饰的变量可以在父子组件之间同步更新。父组件和子组件都可以修改[@Link](/user/Link)变量,且修改会相互影响。

  4. @Observed:用于标记一个类为可观察类。当[@Observed](/user/Observed)类的实例发生变化时,依赖该实例的组件会自动重新渲染。

  5. @ObjectLink:用于在组件中引用[@Observed](/user/Observed)类的实例。[@ObjectLink](/user/ObjectLink)修饰的变量会与[@Observed](/user/Observed)类的实例保持同步,当实例发生变化时,组件会自动更新。

这些装饰器共同构成了鸿蒙Next中的状态管理机制,使得开发者可以高效地管理组件的状态,并实现组件之间的数据传递和同步。

更多关于HarmonyOS鸿蒙Next中关于状态管理的学习的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,状态管理是应用开发的核心部分,主要通过@State@Prop@Link等装饰器实现。@State用于组件内部状态管理,@Prop用于父组件向子组件传递不可变数据,@Link用于父子组件间的双向绑定。此外,@Observed@ObjectLink用于管理复杂对象的状态变化。开发者应合理使用这些装饰器,确保UI与数据的一致性,提升应用性能。

回到顶部