【鸿蒙 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%')
  }
}
相关推荐
Luke Ewin3 小时前
FunASR的Java实现Paraformer实时语音识别 | 一款无需联网的本地实时字幕软件
java·人工智能·语音识别·asr·funasr·paraformer·sensevoice
程序员码歌3 小时前
零代码AI编程实战-热搜从0到1技术方案
前端·ai编程·cursor
叫我阿柒啊3 小时前
从Java全栈到前端框架的全面实战:一次真实面试的深度解析
java·spring boot·缓存·微服务·消息队列·vue3·rest api
望未来无悔3 小时前
系统学习算法 专题十八 队列+宽搜
java·算法
kk不中嘞3 小时前
浅谈前端框架
前端·vue.js·react.js·前端框架
Linlichaoblms4 小时前
Nginx性能调优:参数详解与压测对比
java·spring boot·nginx
服务端技术栈4 小时前
历时 1 个多月,我的第一个微信小程序「图片转 Excel」终于上线了!
前端·后端·微信小程序
一个很老的小萌新4 小时前
json 解析 [{“id“:1,“name“:“apple“},{“id“:2,“name“:“banana“}]
java·前端·json
是2的10次方啊4 小时前
🏗️ 线程池深度解析:ThreadPoolExecutor底层实现与CompletableFuture异步编程实战
java