在 Loxodon Framework 中,ViewModel 默认按页面(Page)生命周期创建,导致跨页面共享数据时出现状态不一致或重复初始化问题。常见误区是直接在多个 ViewModel 中实例化同一业务逻辑类,但因缺乏统一作用域管理,造成数据不同步、事件订阅丢失或内存泄漏。例如:A 页面的 ViewModel 修改了用户登录状态,B 页面的 ViewModel 无法感知变更;或使用 Messenger 通信时因页面销毁未及时解注册,引发空引用异常。此外,框架虽支持 `IContainer` 依赖注入,但若未正确配置为 `Singleton` 或 `Scoped` 生命周期,跨页共享的 `ObservableProperty` 或 `RelayCommand` 将无法实时响应。开发者常忽略 `ViewModelLocator` 的注册策略与页面导航上下文的绑定关系,导致 ViewModel 实例隔离而非复用。如何在保持 MVVM 解耦前提下,安全、高效地实现跨页面状态同步与事件通信,是实际开发中的高频痛点。
1条回答 默认 最新
薄荷白开水 2026-04-11 11:25关注```html一、现象层:跨页面状态不一致的典型表现
- A 页面登录成功后更新
CurrentUser,B 页面 ViewModel 初始化时仍读取空用户; - 使用
Messenger.Default.Send<UserLoggedInMessage>(),但 B 页面未注册或已销毁导致消息静默丢失; - 多个 ViewModel 各自 new 一个
UserProfileService,造成 Token 刷新冲突与缓存不一致; ObservableProperty<string> Title在 PageA 中修改,PageB 的绑定 UI 无响应;- 导航至新页面后,旧 ViewModel 未被 GC(因 Messenger 强引用 + 未解注册),引发内存泄漏。
二、归因层:生命周期错配与作用域失控的根因分析
Loxodon Framework 默认采用 Page-scoped ViewModel 策略——即每次导航至 Page 时,
ViewModelLocator调用IContainer.Resolve<TViewModel>()创建新实例。其底层依赖链如下:Page → ViewModelLocator → IContainer.Resolve() → 新建 ViewModel → 构造函数注入服务若业务服务(如
IAuthService)注册为Transient,则每个 ViewModel 持有独立实例;若误配为Singleton但未实现线程安全/INotifyPropertyChanged 通知,则状态变更无法穿透到所有订阅者。三、架构层:MVVM 解耦前提下的分层共享策略
层级 职责 推荐生命周期 关键约束 Domain Service 用户认证、配置管理、离线缓存 Singleton必须实现 INotifyPropertyChanged或暴露IObservable<T>Shared State Manager 聚合跨页状态(如 AppSession)Singleton禁止直接持有 Page/ViewModel 引用,仅通过事件/ReactiveProperty 通信 Navigation-Aware ViewModel 需感知导航参数且复用状态的页面 Scoped(绑定至 NavigationStack)需重写 OnNavigatedTo做状态同步,避免构造时初始化四、实施层:五步落地解决方案
- 统一注册核心服务为 Singleton:
container.Register<IAuthService, AuthService>().AsSingleton(); - 改造 ViewModel 构造函数:移除 new 实例,改用注入,并监听 Domain Service 事件:
authService.UserChanged += OnUserChanged; - 弃用原始 Messenger,启用强类型 ReactiveUI.MessageBus 或 Loxodon 内置
WeakMessenger(自动解注册); - 在 App.xaml.cs 中预注册 Shared ViewModel:
ViewModelLocator.Register<AppSessionViewModel>(() => appSessionVm); - 页面级 ViewModel 使用 Lazy 初始化共享状态:
private readonly Lazy<AppSessionViewModel> _session = new(() => ViewModelLocator.Resolve<AppSessionViewModel>());
五、验证层:状态同步性与内存安全双校验流程图
graph TD A[PageA 修改 LoginState] --> B{AppSessionViewModel.NotifyPropertyChanged} B --> C[Notify All Subscribers] C --> D[PageB ViewModel.OnPropertyChanged] D --> E[BindingEngine 更新 UI] E --> F{PageB 导航离开?} F -->|是| G[WeakMessenger 自动清理订阅] F -->|否| H[继续响应后续变更] G --> I[GC 可回收 PageB ViewModel]六、避坑指南:高频反模式清单
- ❌ 在 ViewModel 构造函数中调用
new DataService()—— 破坏 DI 容器作用域控制; - ❌ 将
RelayCommand声明为 static —— 导致跨实例命令执行逻辑错乱; - ❌ 使用
Messenger.Default.Register<T>(this, ...)但未在OnDisappearing中Unregister; - ❌ 在
ViewModelLocator中对同一类型多次Register而未指定 key,造成覆盖注册; - ❌ 将
ObservableProperty绑定到非 INPC 兼容对象(如普通 class 而非ObservableObject子类)。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- A 页面登录成功后更新