欢迎光临
我们一直在努力

鸿蒙开发:状态管理 V2 装饰器与组件说明

 

一、类与属性级

1. @ObservedV2

作用:标记一个类为“可观测类”。只有被 @ObservedV2 装饰的类,其内部用 @Trace 装饰的属性变化才会被框架追踪,从而驱动 UI 更新。

用在:ViewModel 类、需要跨页/持久化的数据类(如 Setting)。

示例:

@ObservedV2
export default class StudentViewModel {
@Trace public name: string = '';
@Trace public age: number = 0;
}

注意:@ObservedV2 只装饰类,不装饰变量或方法。


2. @Trace

作用:标记类中的属性为“可观测属性”。该属性一旦被修改,所有依赖它的 UI 会重新计算并刷新。

用在:@ObservedV2 类内部的成员变量(基本类型或数组等)。

示例:

@ObservedV2
export default class StudentViewModel {
@Trace public name: string = ''; // 变化 → 界面更新
@Trace public age: number = 0;
@Trace public remark: string = '';
}

注意:数组里若装的是自定义类,需配合 @Type 使用(见下)。


3. @Type(类名)

作用:声明某个 @Trace 数组里元素的类型。框架据此做两件事: ① 深层观测数组里每个对象的 @Trace 属性; ② 持久化(PersistenceV2)时正确序列化/反序列化。

用在:@Trace public xxx: SomeViewModel[] 这类数组属性上。

示例:

@ObservedV2
export default class StudentListViewModel {
@Type(StudentViewModel)
@Trace public students: StudentViewModel[] = [];
}

注意:@Type 与 @Trace 一起用在“可观测类的数组”上,缺一可能导致深层不更新或持久化异常。


二、组件状态与传参

4. @Local

作用:表示组件内部状态。变量由当前组件持有,变化时只刷新本组件(及依赖该状态的子组件)。

用在:页面或自定义组件里“仅本组件关心”的数据,如输入框内容、是否展开等。

示例:

@ComponentV2
struct BottomView {
@Local inputName: string = ''; // 仅本组件用,不传给父
@Local inputAge: string = '';
@Local inputGender: string = '男';
}

与 @Param 区别:@Local 是组件自己的状态;@Param 是父组件传进来的。


5. @Param

作用:表示父组件传入的只读参数。子组件用 @Param 声明的变量接收父组件传入的值,默认不能在子组件里修改(若允许“本地改一次”需配合 @Once)。

用在:子组件需要展示或依赖父组件给的数据时。

示例:

@ComponentV2
struct TitleView {
@Param setting: Setting = new Setting(); // 父组件传入
@Param totalCount: number = 0; // 父组件传入
}

父组件用法:

TitleView({ setting: this.setting, totalCount: this.totalCount })


6. @Param @Once

作用(以官方文档为准):

  • “Once”指:父→子只同步一次。变量在初始化时接受父组件传入的值,之后父组件再改这个数据,不会同步给子组件(子保持当时拿到的值)。
  • 子组件:可以在本地多次修改该变量,并触发 UI 刷新,效果类似 @Local,但还能接收父传入的初始值。

所以 @Param @Once 不是“子组件只能改一次”,而是“从父只接收一次,子可随意改”。

本工程演示(主页面顶部):父有 @Local parentNum,按钮「父+1」让父数字递增;同一数据传给两个子组件:

  • 子(Param):仅 @Param value,父改则子跟着变(0→1→2→3…)。
  • 子(Once):@Param @Once value,只同步初始值,父改后子始终显示 0。

注意:

  • 若需要父一改、子就跟变,用普通 @Param 即可,不要加 @Once。

7. @Event

作用:声明子组件向父组件“发事件”的回调。子组件在合适时机(如点击删除)调用该回调,由父组件决定具体逻辑(如从列表移除)。

用在:子组件需要通知父组件做某件事时(删除、选中、提交等)。

示例:

// 子组件
@ComponentV2
struct StudentItem {
@Event onDelete: () => void = () => {}; // 父组件传入的回调
// 点击删除时: this.onDelete();
}

// 父组件
StudentItem({
student: obj.item,
onDelete: () => this.studentList.removeStudent(obj.item)
})


三、计算与监听

8. @Computed

作用:标记一个 getter 为“计算属性”。框架会记录 getter 里用到的可观测状态(依赖),依赖不变时用缓存;依赖一变就重新计算,并刷新所有使用该 getter 的 UI。

用在:由其它状态“算出来”的值(如列表长度、未完成数量、合计等)。

示例:

@Computed
get totalCount(): number {
return this.studentList.students.length; // 依赖 students
}
// 增删学生后,totalCount 自动变,“共 X 人”自动更新

注意:getter 内不要写副作用(如改其它状态),只做纯计算。


9. @Monitor('路径')

作用:监听某个可观测属性的变化,在变化时执行你写的方法(如打日志、上报、同步到别处)。路径用字符串表示,如 'student.name'、'student.remark'。

用在:需要“属性一变就执行一段逻辑”时,而不是只刷新 UI。

示例:

@Monitor('student.name')
onNameChange(mon: IMonitor) {
hilog.info(0x0000, 'StudentList', '姓名: %{public}s -> %{public}s',
String(mon.value()?.before), String(mon.value()?.now));
}

@Monitor('student.remark')
onRemarkChange(mon: IMonitor) {
hilog.info(0x0000, 'StudentList', '备注: %{public}s -> %{public}s',
String(mon.value()?.before), String(mon.value()?.now));
}

注意:只有该路径对应的属性真的发生变化时才会触发;若界面没有修改该属性的操作,不会打印。


四、UI 复用与列表

10. @Builder

作用:把一段 UI 结构抽成可复用的“构建函数”,多处调用,减少重复代码。

用在:多个地方用到的相同或相似 UI(如统一风格的按钮、卡片、表单项)。

示例:

@Builder
function ActionButton(label: string, onClick: () => void) {
Button(label)
.width('100%')
.onClick(onClick)
}

// 使用
ActionButton('设置', () => { … })
ActionButton('添加', () => { … })


11. Repeat

作用:根据数组按项渲染多个子组件,类似“列表循环”。数据源变化时,框架会做增量更新,只更新变化的那几项。

用在:列表、表格行、标签组等“数据驱动的一串子组件”。

示例:

Repeat<StudentViewModel>(this.studentList.students)
.each((obj: RepeatItem<StudentViewModel>) => {
StudentItem({
student: obj.item,
onDelete: () => this.studentList.removeStudent(obj.item)
})
})

注意:本工程中 Repeat.each() 只接受一个参数(构建函数),不支持 keyGenerator 等额外参数。


五、全局与持久化

12. AppStorageV2

作用:应用内全局状态。通过 AppStorageV2.connect(类, 键, 初始化函数) 拿到或创建“单例”,多个页面/Ability 共用同一份数据,改一处处处生效。

用在:跨页面、跨 Ability 的配置或状态(如主题、语言、班级名称、是否显示某类数据)。

示例:

// 定义可观测类
@ObservedV2
export class Setting {
@Trace public className: string = '一年级1班';
}

// 在任意页面连接(无则创建)
@Local setting: Setting = AppStorageV2.connect(Setting, 'Setting', () => new Setting())!;

// 设置页改 className,主页面标题会同步更新

注意:数据只在进程存活期间存在,进程结束就清空;要关应用仍保留需用 PersistenceV2。


13. PersistenceV2

作用:把可观测类的实例持久化到本地,应用退出再打开后,通过 PersistenceV2.connect 取回上次的数据。

用在:需要“关应用再打开仍保留”的列表、表单草稿、用户设置等。

示例:

@Local studentList: StudentListViewModel =
PersistenceV2.connect(StudentListViewModel, 'StudentList', () => new StudentListViewModel())!;

注意:

  • 恢复出来的可能是“普通对象”而非 ViewModel 实例,直接绑定到 UI 可能不刷新。本工程在 aboutToAppear 里做了 rehydrate:若检测到是普通对象,则转成新的 ViewModel 实例再赋回列表。
  • 持久化类需用 @ObservedV2,数组元素类型需用 @Type 声明。

六、本工程中的对应关系速查

装饰器/组件

本工程中的使用位置

@ObservedV2

StudentViewModel、StudentListViewModel、Setting

@Trace

name/age/gender/remark、students、className

@Type

StudentListViewModel.students

@Local

StudentListPage(studentList, setting, parentNum)、BottomView(输入框)、SettingPage(setting)

@Param

主页面 ParamDemoChild(value) 与 OnceDemoChild 对比、TitleView、ListView、StudentItem、BottomView

@Param @Once

主页面 OnceDemoChild(value) 与 ParamDemoChild 对比

@Event

StudentItem(onDelete)

@Computed

StudentListPage(totalCount)

@Monitor

StudentItem(onNameChange, onRemarkChange)

@Builder

BottomView(ActionButton)

Repeat

ListView(学生列表)

AppStorageV2

Setting(班级名称跨页)

PersistenceV2

StudentListViewModel(学生列表持久化)


七、文档中提到但本工程未使用的 V2 相关能力

对照 MVVM 模式(V2) 与状态管理 V2 文档,以下能力在文档中有提及,本工程当前未使用,便于后续扩展时参考:

能力

说明

Repeat 懒加载场景(LazyForEach)

文档中 Repeat 有两种用法:非懒加载(本工程已用 Repeat.each)与懒加载。懒加载适用于长列表、按需渲染,需配合 LazyForEach + 数据源接口使用,本工程列表较短,仅用非懒加载。

@Require

与 @Param 搭配表示必填参数,父组件未传时编译/运行会报错。本工程所有 @Param 均提供了默认值,未使用 @Require。

@Provider / @Consume

状态管理 V2 中的跨层级双向同步方案(祖先 ↔ 后代),与 AppStorageV2 不同。本工程跨页/跨 Ability 用 AppStorageV2,未使用 @Provider/@Consume。

小结:MVVM V2 文档里的核心装饰器(@Local、@Param、@Once、@Event、@ObservedV2、@Trace、@Type、@Monitor、@Computed、@Builder)与能力(Repeat、AppStorageV2、PersistenceV2)本工程均已使用;上述三项为“文档有提、工程未用”的扩展点。


八、参考

  • MVVM 模式(V2)
赞(0)
未经允许不得转载:171主机测评 » 鸿蒙开发:状态管理 V2 装饰器与组件说明
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址