【鸿蒙 NEXT】V1迁移V2状态管理
@State 对应 @Local
@State组件内的状态管理,允许父组件传递的值会覆盖子组件的值。
@Local明确了该装饰器的作用范围,必须本地初始化,不允许外部传入初始化。
@Prop 对应 @Param
@Prop装饰的变量允许本地修改,但修改不会同步回父组件。当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。
@Param装饰的变量支持本地初始化,但不允许在组件内部直接修改。@Param表示组件从外部传入的状态,使得父子组件之间的数据能够进行同步,明确表示这个值是从外部传入的。
@Watch 对应 @Monitor
@Watch用于监听状态变量的变化,并在变量变化时触发指定回调函数。
@Monitor替代了@Watch,可以更灵活地监听变量的变化,并获取变量变化前后的值。
@Link 对应 @Param @Event
@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;// 将迁移标志设置为truemovingState.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%')}
}