mvvm中v和vm关系,vm中v和m的关系?

目录

  • [MVVM 中 View 与 ViewModel 核心关系报告](#MVVM 中 View 与 ViewModel 核心关系报告)
    • [一、宏观说明:V 与 VM 的三大核心关系](#一、宏观说明:V 与 VM 的三大核心关系)
    • [二、分关系代码示例 + 通俗理解](#二、分关系代码示例 + 通俗理解)
      • [1. 关系一:V 主动调用 VM(核心依赖)](#1. 关系一:V 主动调用 VM(核心依赖))
      • [2. 关系二:VM 广播通知 V(反向通信)](#2. 关系二:VM 广播通知 V(反向通信))
      • [3. 关系三:V ↔ VM 双向绑定(属性同步)](#3. 关系三:V ↔ VM 双向绑定(属性同步))
    • 三、宏观总结
    • 四、为什么必须这样梳理、理解?
  • [MVVM ViewModel 内部 V/M 分层精简核心报告](#MVVM ViewModel 内部 V/M 分层精简核心报告)

MVVM 中 View 与 ViewModel 核心关系报告

一、宏观说明:V 与 VM 的三大核心关系

View(视图,页面/UI)和 ViewModel(业务逻辑/数据层)是单向依赖 + 双向通信 ,严格遵守:V 依赖 VM,VM 绝不依赖 V,核心关系只有 3 种:

  1. V 主动调用 VM (核心依赖)
    V 持有 VM 实例,主动读取 VM 的属性 (展示数据)、调用 VM 的方法/命令(触发业务逻辑)。
  2. VM 广播通知 V (反向通信)
    VM 不直接操作 UI,通过事件/消息广播通知,V 自己监听并执行弹窗、跳转、加载等 UI 行为。
  3. V ↔ VM 双向绑定 (数据同步)
    框架自动封装:VM 属性变 → 自动更新 V;V 输入变 → 自动回写 VM,实现属性值同步

二、分关系代码示例 + 通俗理解

1. 关系一:V 主动调用 VM(核心依赖)

理解 :V 是"使用者",VM 是"工具人",V 主动找 VM 拿数据、办事情。

场景:页面显示用户名、点击登录按钮。

csharp 复制代码
// ========== ViewModel(只提供数据和方法,不认识 V) ==========
public class LoginVM
{
    // 公开属性:V 用来读取展示
    public string UserName { get; set; } = "张三";

    // 公开方法:V 用来调用触发业务
    public void DoLogin()
    {
        Console.WriteLine("执行登录逻辑...");
    }
}

// ========== View(主动依赖 VM,调用属性+方法) ==========
public class LoginPage
{
    // V 持有 VM 实例(唯一依赖方向)
    private LoginVM _vm = new LoginVM();

    public void ShowPage()
    {
        // 1. V 调用 VM 属性:读取数据展示
        Console.WriteLine("用户名:" + _vm.UserName);

        // 2. V 调用 VM 方法:触发业务
        _vm.DoLogin();
    }
}

2. 关系二:VM 广播通知 V(反向通信)

理解 :VM 是"电台",只发广播;V 是"收音机",自己听广播自己做事,VM 完全不调用 V

场景:登录成功后弹窗、页面跳转、显示加载动画。

csharp 复制代码
// ========== ViewModel(只发通知,不操作 UI) ==========
public class LoginVM
{
    // 定义广播事件(电台频道)
    public event Action ShowSuccessPopup;

    public void DoLogin()
    {
        Console.WriteLine("登录成功!");
        // VM 广播通知:谁听谁处理,不知道 V 存在
        ShowSuccessPopup?.Invoke();
    }
}

// ========== View(自己监听,自己执行 UI) ==========
public class LoginPage
{
    private LoginVM _vm = new LoginVM();

    public LoginPage()
    {
        // V 主动订阅 VM 的广播
        _vm.ShowSuccessPopup += () =>
        {
            // 所有 UI 操作,全由 V 自己执行
            Console.WriteLine("【UI弹窗】登录成功!");
        };
    }
}

3. 关系三:V ↔ VM 双向绑定(属性同步)

理解 :框架自动帮你写好"监听代码",数据变了自动同步,不用手动赋值,只同步属性值

底层本质:单向调用 + 单向通知的封装语法糖。

csharp 复制代码
// ========== 1. 双向绑定基础:通知属性变化 ==========
public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}

// ========== 2. ViewModel:支持双向绑定的属性 ==========
public class LoginVM : INotifyPropertyChanged
{
    private string _userName;
    public string UserName
    {
        get => _userName;
        set
        {
            _userName = value;
            // VM 通知:我的属性变了
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

// ========== 3. View(框架自动实现双向同步) ==========
public class LoginPage
{
    private LoginVM _vm = new LoginVM();

    public LoginPage()
    {
        // 框架自动做两件事:
        // ① VM 变 → 更新 V(监听通知)
        _vm.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == nameof(_vm.UserName))
                输入框.Text = _vm.UserName;
        };

        // ② V 变 → 写回 VM(主动调用)
        输入框.TextChanged += (s, e) =>
        {
            _vm.UserName = 输入框.Text;
        };
    }
}

三、宏观总结

  1. 唯一依赖V → VM(V 调用 VM 的属性、方法),VM 永远不依赖、不调用 V;
  2. 反向通信:VM 只广播通知,V 自己监听执行 UI 行为;
  3. 双向绑定 :框架封装的数据同步工具,= V 调用 VM + VM 通知 V;
  4. 清晰边界:数据/展示走绑定,交互/业务走调用,UI 行为走通知。

四、为什么必须这样梳理、理解?

  1. 不混乱:把"调用、通知、绑定"彻底分开,再也不会混淆 MVVM 通信逻辑;
  2. 符合设计规范 :严格遵守VM 无 UI 依赖,代码可测试、易维护、不耦合;
  3. 所有框架通用:Vue/React/WPF/MAUI 全是这套逻辑,学会底层思想一通百通;
  4. 写出高质量代码:避免 VM 直接操作 UI(烂代码根源),分工明确、bug 更少;
  5. 夯实架构思维:从"会写代码"升级到"懂设计模式",是进阶程序员的核心能力。

MVVM ViewModel 内部 V/M 分层精简核心报告

一、核心前提

标准MVVM核心规则:View 单向依赖 ViewModel,ViewModel 绝不依赖 View

为解耦 UI 与业务,可将 ViewModel 内部分为两层:

  1. VM-V(视图适配层):对外对接 View,负责所有 UI 交互

  2. VM-M(业务数据层):对内对接 Model,负责纯业务数据逻辑

重点:V 与 VM 的调用、通知、双向绑定,全部属于 VM-V,和 VM-M 无关。

二、两层核心职责(精简版)

1. VM-V 视图适配层(对外)

只做 UI 适配,无纯业务逻辑,包含三类核心内容:

  • 双向绑定属性(UI 数据同步)

  • 公开方法/命令(View 交互调用)

  • UI 通知事件(弹窗、跳转等 UI 行为通知)

2. VM-M 业务数据层(对内)

无任何 UI 依赖,可独立单元测试,负责:网络请求、数据校验、业务计算、原始数据处理,不感知、不操作 UI。

三、VM 内部 V/M 交互黄金规则

  1. 必须交互:VM 作为 UI 与业务的桥梁,两层必须配合完成业务流程。

  2. 单向依赖(铁律) :仅允许 VM-V → VM-M,禁止 VM-M 反向依赖、操作 VM-V。

  3. 交互归属 :所有两层交互代码全部写在 VM-V。VM-V 主动调用业务、接收数据、适配 UI、触发通知;VM-M 只负责执行业务、返回纯数据。

四、核心精简代码示例

1. 底层数据与 VM-M(纯业务)

csharp 复制代码
// 数据模型
public class UserModel
{
    public string Account { get; set; }
    public string NickName { get; set; }
}

// VM-M:纯业务,无UI依赖
public class VmModelPart
{
    public async Task<UserModel> LoginAsync(string account, string pwd)
    {
        await Task.Delay(100);
        return new UserModel { Account = account, NickName = "登录用户" };
    }
}

2. 绑定基类(双向绑定基础)

csharp 复制代码
public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; }
public class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropChanged(string propName)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

3. VM-V(核心交互层)

csharp 复制代码
public class VmViewPart : BindableBase
{
    // 单向依赖业务层
    private readonly VmModelPart _bizPart = new VmModelPart();

    // 双向绑定属性
    private string _inputAccount;
    public string InputAccount
    {
        get => _inputAccount;
        set { _inputAccount = value; RaisePropChanged(nameof(InputAccount)); }
    }

    private string _showNickName;
    public string ShowNickName
    {
        get => _showNickName;
        set { _showNickName = value; RaisePropChanged(nameof(ShowNickName)); }
    }

    // UI通知事件
    public event Action LoginSuccessEvent;

    // 供View调用的入口方法
    public async void Login()
    {
        // 调用业务逻辑
        var res = await _bizPart.LoginAsync(InputAccount, "123456");
        // 适配更新UI数据
        ShowNickName = res.NickName;
        // 通知View执行UI操作
        LoginSuccessEvent?.Invoke();
    }
}

4. View 层(仅对接 VM-V)

csharp 复制代码
public class LoginView
{
    private readonly VmViewPart _vm = new VmViewPart();

    public LoginView()
    {
        // 监听通知,自主执行UI逻辑
        _vm.LoginSuccessEvent += () => Console.WriteLine("View:执行弹窗/跳转");
    }

    // 页面交互调用VM
    public void OnLoginClick() => _vm.Login();
}

五、全链路极简流程

  1. View → VM-V:用户交互,调用方法、同步表单数据

  2. VM-V → VM-M:触发纯业务逻辑

  3. VM-M → VM-V:返回纯数据,无UI操作

  4. VM-V → View:更新绑定数据、广播UI通知

六、核心禁忌(重点)

禁止 VM-M 操作/依赖 VM-V:业务层一旦触碰 UI 属性、UI 事件,会造成架构耦合、无法单元测试、维护成本剧增。

七、梳理该架构的核心意义

  1. 解耦清晰:UI 交互与纯业务逻辑完全隔离,互不干扰;

  2. 可测可维护:VM-M 无UI依赖,可独立单元测试,UI迭代不改动业务;

  3. 代码规范统一:UI逻辑收敛VM-V,业务逻辑收敛VM-M,结构清晰;

  4. 打通架构闭环:彻底理解MVVM内外层交互原理,摆脱只会用不懂底层的问题。

八、最终一句话总结

ViewModel 内部分为视图适配(VM-V)和业务数据(VM-M)两层,所有 UI 交互归属 VM-V,VM-V 单向调用 VM-M,业务层零 UI 依赖,实现 MVVM 最大解耦优势。

相关推荐
阿昌喜欢吃黄桃1 小时前
Java优质开源AI项目
java·ai·langchain·开源·rag·springai·langchain4j
SilentSamsara1 小时前
缓存策略实战:Redis + Python 多级缓存设计与失效策略
开发语言·redis·python·缓存·性能优化
zlinear数据采集卡1 小时前
输出短路保护电路深度解析:从电源的“最后一道防线”到ZLinear采集卡的硬核守护实战
开发语言·嵌入式硬件·持续集成
剑锋所指,所向披靡!1 小时前
C++多线程实现
开发语言·c++·chrome
biubiubiu07061 小时前
SpringBoot3.5.4 AOP环绕通知使用
java·spring boot
十五年专注C++开发1 小时前
Qt之QScopedPointer、QScopeGuard、QScopedValueRollback使用及源码解读
开发语言·c++·qt·qscopedpointer·qscopeguard
fox_lht1 小时前
13.3.测试的组织方式
开发语言·后端·rust
一天 24h1 小时前
Vue3父子组件传值:从零到精通
前端·javascript·vue.js·pycharm·npm·学习方法
西安邮电大学2 小时前
Redis四大经典缓存问题
java·redis·后端·其他·面试