普通网友 2026-04-11 11:25 采纳率: 98.5%
浏览 0
已采纳

LoxodonFramework中ViewModel如何实现跨页面数据共享?

在 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聚合跨页状态(如 AppSessionSingleton禁止直接持有 Page/ViewModel 引用,仅通过事件/ReactiveProperty 通信
    Navigation-Aware ViewModel需感知导航参数且复用状态的页面Scoped(绑定至 NavigationStack)需重写 OnNavigatedTo 做状态同步,避免构造时初始化

    四、实施层:五步落地解决方案

    1. 统一注册核心服务为 Singleton
      container.Register<IAuthService, AuthService>().AsSingleton();
    2. 改造 ViewModel 构造函数:移除 new 实例,改用注入,并监听 Domain Service 事件:
      authService.UserChanged += OnUserChanged;
    3. 弃用原始 Messenger,启用强类型 ReactiveUI.MessageBus 或 Loxodon 内置 WeakMessenger(自动解注册);
    4. 在 App.xaml.cs 中预注册 Shared ViewModel
      ViewModelLocator.Register<AppSessionViewModel>(() => appSessionVm);
    5. 页面级 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, ...) 但未在 OnDisappearingUnregister
    • ❌ 在 ViewModelLocator 中对同一类型多次 Register 而未指定 key,造成覆盖注册;
    • ❌ 将 ObservableProperty 绑定到非 INPC 兼容对象(如普通 class 而非 ObservableObject 子类)。
    ```
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 今天
  • 创建了问题 4月11日