HarmonyOS鸿蒙Next中项目 MVVM 架构分层—ViewModel 该放在哪一层?@ObservedV2 的 Model 和 ViewModel 边界怎么划?
HarmonyOS鸿蒙Next中项目 MVVM 架构分层—ViewModel 该放在哪一层?@ObservedV2 的 Model 和 ViewModel 边界怎么划? 团队在搭建一个中大型 HarmonyOS 应用,想用 MVVM 分层来管理代码结构。但 ArkUI 的状态管理机制(尤其是 V2 装饰器)和传统 MVVM 有些微妙的不兼容,落地时遇到不少困惑。
多个页面需要共享用户信息、主题配置等全局状态。用 AppStorage 存 @ObservedV2 对象可行吗?AppStorage 对 @ObservedV2 对象的观测深度有没有已知限制?
更多关于HarmonyOS鸿蒙Next中项目 MVVM 架构分层—ViewModel 该放在哪一层?@ObservedV2 的 Model 和 ViewModel 边界怎么划?的实战教程也可以访问 https://www.itying.com/category-93-b0.html
我一般的做法是:
ViewModel 放在 viewmodel 或 store 层,不放 model 层。它面向页面或业务场景,负责组装页面状态、调用 service/repository、暴露事件方法。Model 只表达业务数据和数据访问结果,比如接口 DTO、数据库实体、领域对象,不直接服务 UI 刷新。
@ObservedV2 不是“Model 标记”,它只是告诉 ArkUI 这个 class 可以被状态系统接管。真正会触发刷新的还是 @Trace。所以边界不是看有没有 @ObservedV2,而是看职责:
- 接口返回的
UserEntity、ConfigDTO:放 Model,不建议绑 ArkUI 装饰器。 - 页面要展示和编辑的
UserProfileVM、ThemeState:可以[@ObservedV2](/user/ObservedV2) + [@Trace](/user/Trace)。 - 多页面共享的登录态、主题:单独做
AppState/UserSessionState,放全局状态层,不塞到普通 Model 里。
多个页面共享用户信息、主题配置,V2 里优先用 AppStorageV2.connect,不是老的 AppStorage。AppStorage 和 AppStorageV2 数据不互通,混着用后面会很难排查。
AppStorageV2 存 @ObservedV2 对象是可行的,典型写法就是一个全局状态类:
[@ObservedV2](/user/ObservedV2)
class AppState {
[@Trace](/user/Trace) userName: string = ''
[@Trace](/user/Trace) theme: string = 'light'
}
然后各页面用同一个 key connect。但要注意,@ObservedV2 本身不负责深度刷新,字段要加 @Trace。嵌套对象也一样,外层字段要 @Trace,内层 class 也要 @ObservedV2,内层需要刷新的字段也要 @Trace。没加 @Trace 的字段,别指望 UI 会自动同步。
我的落地建议是:全局状态只放轻量、稳定、跨 Ability 或跨页面确实共享的 UI 状态。用户详情大对象、配置大缓存还是放业务缓存或持久化层,AppStorageV2 里放必要字段、id、版本号。这样 MVVM 边界清楚,状态刷新也可控。
更多关于HarmonyOS鸿蒙Next中项目 MVVM 架构分层—ViewModel 该放在哪一层?@ObservedV2 的 Model 和 ViewModel 边界怎么划?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
感谢
AppStorage 更适合放轻量、全局、生命周期明确的状态键值,不建议直接塞很大的 @ObservedV2 对象树。多个页面共享用户信息/主题配置时,可以把 userId、themeMode、configVersion 这类轻量字段放 AppStorage;真正的用户详情和配置对象放到 store/repository,由页面 ViewModel 订阅或拉取。
@ObservedV2 的边界建议这样划:Model 负责可观察数据结构,字段用 @Trace 表示 UI 依赖;ViewModel 负责组合数据、调用 repository、处理交互事件;View 只读 ViewModel 暴露的状态并派发事件。不要让 Model 反向依赖页面生命周期,也不要把 AppStorage 当数据库或大对象缓存。
从大家回答中学习
- ViewModel:负责管理UI状态和交互逻辑。作为连接Model和View的桥梁,ViewModel监控Model数据的变化,通知View更新UI,同时处理用户交互事件并转换为数据操作。
// src/main/ets/viewmodel/TaskViewModel.ets
import TaskModel from '../model/TaskModel';
@ObservedV2
export default class TaskViewModel {
@Trace public taskName: string = 'Todo';
@Trace public isFinish: boolean = false;
updateTask(task: TaskModel) {
this.taskName = task.taskName;
this.isFinish = task.isFinish;
}
updateIsFinish(): void {
this.isFinish = !this.isFinish;
}
}
/src
├── /main │ ├── /ets │ │ ├── /entryability │ │ ├── /model │ │ │ ├── TaskListModel.ets │ │ │ └── TaskModel.ets │ │ ├── /pages │ │ │ ├── SettingPage.ets │ │ │ └── TodoListPage.ets │ │ ├── /settingability │ │ ├── /view │ │ │ ├── BottomView.ets │ │ │ ├── ListView.ets │ │ │ └── TitleView.ets │ │ ├── /viewmodel │ │ │ ├── TaskListViewModel.ets │ │ │ └── TaskViewModel.ets │ └── /resources │ ├── … ├─── …
学习了
在HarmonyOS Next MVVM中,ViewModel属于领域层或业务逻辑层,不直接持有视图引用。
@ObservedV2装饰的Model应只包含纯数据与状态,不包含业务逻辑;ViewModel负责状态管理、业务编排,通过@ObservedV2装饰自身属性,并调用Model层更新。
边界:Model仅存储数据,ViewModel组合Model并暴露给UI层。
在 HarmonyOS Next 中,ViewModel 位于业务逻辑层,但在 ArkUI V2 体系下它直接持有并暴露用 @ObservedV2 / @Trace 装饰的状态数据,不像传统 MVVM 那样中间隔一层 LiveData 或 Observable。它负责:将原始 Model 数据转化为 View 可直接消费的响应式状态,并封装 UI 逻辑(按钮可用性、格式转换等)。
@ObservedV2 的 Model 和 ViewModel 边界划分规则:
- Model 是纯数据类,用
@ObservedV2装饰,内部属性用@Trace;不应包含 UI 逻辑或 UI 专用状态。 - ViewModel 持有并代理 Model,用
@Trace暴露那些 View 直接绑定的可观察属性(可以是 Model 的字段拷贝,也可以是 Model 引用)。如果使用对象引用传递,直接在 View 中在@Local变量上赋值 Model 对象;状态跟踪深度由@ObservedV2链保证,无需包装。
关于全局状态:
AppStorage 存储 @ObservedV2 对象是可行且推荐的,这是官方支持的全局共享方式。
观测深度无已知限制,只要对象被 @ObservedV2 装饰且其可变属性用 @Trace 标记,AppStorage 会跟踪整个链路。取出后赋值给 @Local 或 @Param @Once,View 即可响应任意深层变化。

