Skip to content

MVVM 架构

MVC 的问题在哪?

理解 MVVM 之前,需要先看 MVC 的职责划分。MVC 把应用拆成三层:Model(数据)、View(视图)、Controller(控制器)。用户操作 View,Controller 接收事件后更新 Model,Model 变化后再刷新 View。

早期后端 MVC 在模板引擎渲染场景下比较自然,但迁移到前端后,Controller 往往同时承担 DOM 操作和业务逻辑管理两类职责。随着页面交互增多,代码容易出现职责混杂、状态分散、维护成本上升的问题。每次数据变化都需要手动调用 document.getElementById(...) 等 API 更新 DOM,开发者需要同时关注数据状态和视图同步,复杂度会快速增加。

MVVM 做了什么

MVVM 将原本分散在 Controller 中的视图同步职责抽象为 ViewModel。ViewModel 的核心能力是双向数据绑定:数据变化后视图自动更新,用户输入后数据自动同步。开发者无需直接维护大量 DOM 更新逻辑,框架会负责完成数据与视图之间的同步。

下面用一个简化实现说明 ViewModel 的工作方式:

html
<!-- View 层 -->
<input id="input" />
<div id="content"></div>

<script>
  window.onload = () => {
    // Model 层:存数据
    const data = { inputVal: '' }

    // ViewModel 层:数据 ↔ 视图 的桥梁
    const input = document.getElementById('input')
    input.addEventListener('input', (e) => {
      proxy.inputVal = input.value  // 视图 → 数据
    })

    const proxy = new Proxy(data, {
      set: (target, key, value) => {
        if (key === 'inputVal') {
          document.getElementById('content').innerHTML = value // 数据 → 视图
        }
        return true
      }
    })
  }
</script>

输入框内容变化后,展示区域会同步更新,这体现了 ViewModel 在数据层和视图层之间承担的桥接作用。

Vue2 和 Vue3 实现 MVVM 的差异

Vue2 使用 Object.defineProperty 做数据劫持,但它在监听数组下标变化和新增对象属性时存在天然限制,因此需要 $set 这类补充 API。Vue3 改用 Proxy,直接代理整个对象,新增属性、删除属性都能被拦截,响应式覆盖范围更完整。

数据流向图

View (DOM)  ←→  ViewModel (数据绑定)  ←→  Model (纯数据)

View 负责展示,Model 负责保存数据,ViewModel 负责数据与视图之间的同步。各层职责清晰后,视图变化、数据结构调整和同步逻辑可以相对独立地演进。