【HarmonyOS Next】鸿蒙状态管理装饰器V1和V2混用方案
一、V1和V2为什么需要混用
自从api7开始,一直到api10。V1的实际使用中,开发人员发现@Observed和@ObjectLink 监听实现多层级嵌套对象的更新的方案,太过于臃肿。当需要监听处理更新的多层级对象是七八层,就需要配套创建七八层的ObjectLink,代码太过于冗余。
V2就是为了解决该问题,华为官方才提出的新状态管理装饰器方案。该方案在解决该问题的基础上,也对V1的进行加强。
但是考虑现实开发情况。基本上鸿蒙现有的应用都在24年930节点之前上架了。大量使用的都是V1方案。如果要迁移到V2上。改动量太大,不亚于重新开发界面刷新相关业务。
所以官方V1和V2目前是相关独立,也可混用。这种状态估计会持续很长时间。
但是不用V2,前面提到的嵌套更新问题又需要好的方案解决。所以就需要V1和V2混用。
当然如果是新项目,还是建议直接使用V2.
二、V1和V2混用方案
原则上,只使用@ObservedV2与@Trace装饰器来解决深层嵌套对象更新UI的问题。暂时不使用其他的V2装饰器,其他的状态管理依旧使用V1即可。
以下代码示例参考,只使用@ObservedV2与@Trace装饰器,解决深层嵌套对象更新UI的问题:
import {
util } from '@kit.ArkTS';
/**
* 三级数据结构
*/
class GrandsonInfo {
content: string = "";
}
/**
* 二级数据结构
*/
class ChildInfo {
index: number;
grandsonInfo: GrandsonInfo;
constructor(index: number, content: string) {
this.index = index;
this.grandsonInfo = new GrandsonInfo();
this.grandsonInfo.content = content;
}
}
/**
* 一级数据结构
*/
class ItemInfo {
key: string = util.generateRandomUUID(true);
name: string;
icon: Resource;
childInfo: ChildInfo;
select: boolean;
constructor(name: string, icon: Resource, index: number, content: string) {
this.name = name;
this.icon = icon;
this.childInfo = new ChildInfo(index, content);
this.select = false;
}
}
/**
* 多层嵌套刷新渲染
*/
struct ObservedPage {
private TAG: string = "ObservedPage";
// @State无法混用,运行时报错
// @State mListData: Array<ItemInfo> = [];
mListData: Array<ItemInfo> = [];
aboutToAppear(): void {
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 1, "鹅厂1"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 2, "鹅厂2"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 3, "鹅厂3"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 4, "鹅厂4"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconA"), 5, "鹅厂5"));
this.mListData.push(new ItemInfo('游戏', $r("app.media.iconB"), 6, "鹅厂6"));
}
build() {
List() {
ForEach(this.mListData, (item: ItemInfo, index: number) => {
ListItem() {
Row() {
Image(this.item.icon)
.width(px2vp(200))
.height(px2vp(200))
Text(this.item.name + "(" + this.item.childInfo.index + ")" + " [ " + this.item.childInfo.grandsonInfo.content + " ] ")
.fontSize(px2fp(52))
Blank()
if(this.isLog(this.item, this.index)){
if(this.item.select){
Image($r("app.media.icon_check"))
.size({
width: px2vp(72),
height: px2vp(72)
})
}
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
.onClick(()=>{
this.item.select = !this.item.select;
if(this.item.select){
// 使用很方便,只需要直接改变item数据的任意层级属性值,变化就会同步刷新
this.item.childInfo.index = 666;
this.item.childInfo.grandsonInfo.content = "鹅厂23333"
}else{
this.item.childInfo.index = this.index;
this.item.childInfo.grandsonInfo.content = "鹅厂" + this.index;
}
console.log(this.TAG, " ItemView onClick: " + this.index + " item.select: " + this.item.select);
})
}
}, (item: ItemInfo) => JSON.stringify(item))
}
.width("100%")
.height("100%")
.padding({
left: px2vp(60), right: px2vp(60) })
}
}
三、V1和V2混用注意点
- 使用@ObservedV2与@Trace装饰的类不能和@State等V1的装饰器混合使用,编译时报错。
- 继承自@ObservedV2的类无法和@State等V1的装饰器混用,运行时报错。
- @ObservedV2的类实例目前不支持使用JSON.stringify进行序列化。
- @ObservedV2、@Trace不能与@Observed、@Track混合使用。
- @Trace是class中属性的装饰器,不能用在struct中。
- @Trace不能用在没有被@ObservedV2装饰的class上。
- @ObservedV2仅能装饰class,无法装饰自定义组件。
- 非@Trace装饰的成员属性用在UI上无法触发UI刷新。