【鸿蒙 NEXT】V1迁移V2状态管理

@State 对应 @Local

@State组件内的状态管理,允许父组件传递的值会覆盖子组件的值。

@Local明确了该装饰器的作用范围,必须本地初始化,不允许外部传入初始化。

@Prop 对应 @Param

@Prop装饰的变量允许本地修改,但修改不会同步回父组件。当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。

@Param装饰的变量支持本地初始化,但不允许在组件内部直接修改。@Param表示组件从外部传入的状态,使得父子组件之间的数据能够进行同步,明确表示这个值是从外部传入的

@Watch 对应 @Monitor

@Watch用于监听状态变量的变化,并在变量变化时触发指定回调函数。

@Monitor替代了@Watch,可以更灵活地监听变量的变化,并获取变量变化前后的值。

@Link 装饰的变量,双向绑定,父组件中修改数据影响子组件变量,子组件中修改数据影响父组件变量。当嵌套层级深、状态复杂时,容易引发隐式的多点修改,难以维护。状态变更路径不清晰,多个子组件可能同时修改父状态,造成数据来源不透明。因为双向绑定,每次状态改动会触发同步,可能导致不必要的 UI 重绘。

@Param @Event单向数据流设计思想,子组件中使用@Event定义回调函数,父组件传递@Event 回调函数的实现,当子组件调用回调函数,父组件中执行回调的实现对数据进行更改,此时父组件中的@Local 数据修改,子组件中的@Param 数据也会跟着修改,这样父组件就是数据传递的唯一状态源,所有状态修改路径清晰,便于调试和维护。

@Provide @Consume 对应 @Provider @Consumer

@Consume不支持function,观察第一层变化,如果要观察嵌套场景,配合@Observed和@ObjectLink一起使用。

@Provide允许从父组件初始化

@Consumer支持function,仅能观察自身赋值变化,如果要观察嵌套场景,配合@Trace一起使用。

@Provider不允许从父组件初始化

@LocalStorage 对应 @ObservedV2 @Trace:

@LocalStorage常在自定义window中使用:

复制代码
let storage: LocalStorage = new LocalStorage();
storage.setOrCreate('model', model);
//为悬浮窗加载页面内容,这里可以设置在main_pages.json中配置的页面
QDSMSWindow.windowClass.loadContent("pages/QDSMSWindowContent", storage, (err) => {

let storage = LocalStorage.getShared()

@Preview
@Entry(storage)
@Component
struct QDSMSWindowContent {
  @LocalStorageProp('model') model: QDKeyboardParamsModel = new QDKeyboardParamsModel()

@ObservedV2 @Trace 加单例替代@LocalStorage:

使用export class定义单例类

复制代码
// storage.ets
@ObservedV2
export class MyStorage {
  static singleton_: MyStorage;
  static instance() {
    if(!MyStorage.singleton_) {
      MyStorage.singleton_ = new MyStorage();
    };
    return MyStorage.singleton_;
  }
  @Trace count: number = 47;
}

import使用:

复制代码
// Page1.ets
import { MyStorage } from './storage';

@Entry
@ComponentV2
struct Page1 {
  storage: MyStorage = MyStorage.instance();	
  build() {
    Column() {
        Text(`${this.storage.count}`).onClick(() => {
            this.storage.count++;
          })
    }
  }
}

对于状态管理V2,状态变量的观察能力内嵌到数据本身,不再和View层耦合,所以对于状态管理V2,不再需要类似LocalStorage的能力,可以使用创建@ObservedV2和@Trace装饰类的实例,由开发者自己import和export,自己实现状态变量的页面间共享。

@AppStorage:

复制代码
AppStorage.setOrCreate(QDAppStorageKeys.QDLaunchOpenDialog, false)
@StorageLink("QDLaunchOpenDialog") @Watch('showPrivacyWindow') openDialog: boolean = false

定义对象配合@ObservedV2 @Trace 实现@AppStorage效果,并使用AppStorageV2.connect:

复制代码
import { common, Want } from '@kit.AbilityKit';
import { AppStorageV2 } from '@kit.ArkUI';

@ObservedV2
export class MyStorage {
  @Trace count: number = 0
}

@Entry
@ComponentV2
struct Index1 {
  @Local storage: MyStorage = AppStorageV2.connect(MyStorage, 'storage', () => new MyStorage())!;

    build() {
      Column() {
        Text(`EntryAbility1 count: ${this.storage.count}`)
          .fontSize(50)
          .onClick(() => {
            this.storage.count++;
          })
      }
    }
}

@PersistentStorage:

PersistentStorage持久化的触发时机依赖AppStorage的观察能力,且与AppStorage耦合,开发者无法自主选择写入或读取持久化数据的时机。

PersistentStorage使用序列化和反序列化,并没有传入类型,所以在持久化后,会丢失其类型,且对象的属性方法不能持久化。

复制代码
class data {
  name: string = 'ZhangSan';
  id: number = 0;
}

PersistentStorage.persistProp('numProp', 47);
PersistentStorage.persistProp('dataProp', new data());

@Entry
@Component
struct Index {
  @StorageLink('numProp') numProp: number = 48;
  @StorageLink('dataProp') dataProp: data = new data();

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

      // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
      Text(`dataProp.name: ${this.dataProp.name}`)
        .onClick(() => {
          this.dataProp.name += 'a';
        })
        .fontSize(30)
      // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
      Text(`dataProp.id: ${this.dataProp.id}`)
        .onClick(() => {
          this.dataProp.id += 1;
        })
        .fontSize(30)

    }
    .width('100%')
  }
}

与PersistenceV2关联的@ObservedV2对象,其@Trace属性的变化,会触发整个关联对象的自动持久化。

开发者也可以调用PersistenceV2.save和PersistenceV2.globalConnect接口来手动触发持久化写入和读取。

复制代码
// 迁移到globalConnect
import { PersistenceV2, Type } from '@kit.ArkUI';

// 接受序列化失败的回调
PersistenceV2.notifyOnError((key: string, reason: string, msg: string) => {
  console.error(`error key: ${key}, reason: ${reason}, message: ${msg}`);
});

class Data {
  name: string = 'ZhangSan';
  id: number = 0;
}

@ObservedV2
class V2Data {
  @Trace name: string = '';
  @Trace Id: number = 1;
}

@ObservedV2
export class Sample {
  // 对于复杂对象需要@Type修饰,确保序列化成功
  @Type(V2Data)
  @Trace num: number = 1;
  @Trace V2: V2Data = new V2Data();
}

// 用于判断是否完成数据迁移的辅助数据
@ObservedV2
class StorageState {
  @Trace isCompleteMoving: boolean = false;
}

function move() {
  let movingState = PersistenceV2.globalConnect({type: StorageState, defaultCreator: () => new StorageState()})!;
  if (!movingState.isCompleteMoving) {
    PersistentStorage.persistProp('numProp', 47);
    PersistentStorage.persistProp('dataProp', new Data());
    let num = AppStorage.get<number>('numProp')!;
    let V1Data = AppStorage.get<Data>('dataProp')!;
    PersistentStorage.deleteProp('numProp');
    PersistentStorage.deleteProp('dataProp');

    // V2创建对应数据
    let migrate = PersistenceV2.globalConnect({type: Sample, key: 'connect2', defaultCreator: () => new Sample()})!;  // 使用默认构造函数也可以
    // 赋值数据,@Trace修饰的会自动保存,对于非@Trace对象,也可以调用save保存,如:PersistenceV2.save('connect2'); 
    migrate.num = num;
    migrate.V2.name = V1Data.name;
    migrate.V2.Id = V1Data.id;

    // 将迁移标志设置为true
    movingState.isCompleteMoving = true;
  }
}

move();

@Entry
@ComponentV2
struct Page1 {
  @Local refresh: number = 0;
  // 使用key:connect2存入数据
  @Local p: Sample = PersistenceV2.globalConnect({type: Sample, key:'connect2', defaultCreator:() => new Sample()})!;

  build() {
    Column({space: 5}) {
      // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
      Text(`numProp: ${this.p.num}`)
        .onClick(() => {
          this.p.num += 1;
        })
        .fontSize(30)

      // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
      Text(`dataProp.name: ${this.p.V2.name}`)
        .onClick(() => {
          this.p.V2.name += 'a';
        })
        .fontSize(30)
      // 应用退出时会保存当前结果。重新启动后,会显示上一次的保存结果
      Text(`dataProp.id: ${this.p.V2.Id}`)
        .onClick(() => {
          this.p.V2.Id += 1;
        })
        .fontSize(30)
    }
    .width('100%')
  }
}
相关推荐
世人万千丶21 小时前
Flutter 框架跨平台鸿蒙开发 - 恐惧清单应用
学习·flutter·华为·开源·harmonyos·鸿蒙
云烟成雨TD21 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
行乾21 小时前
鸿蒙端 IMSDK 架构探索
架构·harmonyos
于慨21 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz21 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
swg32132121 小时前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
从前慢丶21 小时前
前端交互规范(Web 端)
前端
Utopia^21 小时前
Flutter 框架跨平台鸿蒙开发 - 21天挑战
flutter·华为·harmonyos
gelald21 小时前
SpringBoot - 自动配置原理
java·spring boot·后端
殷紫川21 小时前
深入理解 AQS:从架构到实现,解锁 Java 并发编程的核心密钥
java