MVVM(Model-View-ViewModel)和 MVC(Model-View-Controller)是两种常见的设计模式,它们都用于分离应用程序的逻辑、视图和数据,
MVVM
「MVVM」(Model-View-ViewModel)是一种软件架构模式,用于分离应用程序的 UI「view」、业务逻辑「model」和界面逻辑「viewmodel」。
它在设计模式中被广泛用于构建响应式用户界面,尤其适用于现代的单页面应用(SPA)和移动端应用。「MVVM」 模式的主要特点是通过数据绑定机制实现视图和模型之间的松耦合,使得开发者可以专注于业务逻辑和界面展示。
组成部分
Model
模型是应用程序的数据层,代表了应用程序的核心数据和业务逻辑。它直接处理数据存储、获取、更新等操作,并且与视图无关 。
它不关心如何展示数据,只关注如何处理和存储数据(例如通过数据库交互、网络请求等)。
View
视图负责将数据以图形化的方式展示给用户。它是用户界面(UI)的一部分,接收用户输入并向用户展示信息。它只关心如何展示数据,并通过数据绑定机制展示来自 ViewModel 的数据。视图本身不包含任何业务逻辑或数据处理。如按钮、文本框、列表、图表等 UI 元素等。
ViewModel
视图模型是 MVVM 的核心,充当视图和模型之间的中介。它负责从模型中获取数据,并将数据转换为视图能够展示的格式。视图模型还包含与视图相关的行为逻辑和状态管理。
它提供视图需要的数据,并进行适当的数据格式化和处理(例如从模型数据转换为视图友好的格式),并通过数据绑定和观察者模式(如 Observer 或 Pub/Sub)与视图保持同步。
视图模型可以通过属性改变来自动通知视图进行更新。
工作原理
MVVM 模式的工作原理基于数据绑定和观察者模式,通过这种方式实现视图、视图模型和模型之间的自动同步。
视图和视图模型之间的数据绑定
视图模型中的属性与视图中的 UI 元素(如文本框、按钮、标签等)通过数据绑定进行关联。当视图模型的某个属性发生变化时,视图会自动更新展示的内容,无需手动干预。
视图和视图模型之间的双向数据绑定
「双向数据绑定」(Two-way data binding)使得视图和视图模型的状态始终保持同步。当用户在视图中做出改变(例如输入数据、选择项)时,视图会将这些变化自动反映到视图模型中。反之,当视图模型中的数据发生变化时,视图会自动更新。
Vue
中使用了语法糖 v-model
实现了受控表单的双向数据绑定,这种绑定机制使得开发者无需在视图和视图模型之间手动同步数据,减少了冗余的代码和手动操作,提高了开发效率。
视图模型和模型的单向数据流
视图模型通过调用模型的方法来获取和操作数据。模型本身并不关心视图,也不直接向视图发送数据更新请求。视图模型处理所有与视图相关的逻辑,并在必要时更新模型数据。
优点
分离关注点
MVVM 模式通过将数据(模型)、业务逻辑(视图模型)和视图(UI)分离,提高了应用程序的可维护性。各个部分之间的解耦使得修改和扩展变得更加容易。
自动更新和数据绑定
通过自动化的数据绑定,视图和视图模型之间的同步变得更加容易。只要视图模型中的数据发生变化,视图就会自动更新,而无需手动操作 UI 元素
更好的开发效率
MVVM 模式使得开发者可以更加专注于业务逻辑和数据的处理,而不需要频繁编写视图与模型之间的同步代码,减少了代码冗余,提高了开发效率。
缺点
复杂性
对于简单的应用程序或小型项目,MVVM 模式可能显得过于复杂,数据绑定和视图模型的引入可能导致不必要的开销。
性能问题
虽然 MVVM 提高了开发效率,但数据绑定和观察者模式在大量数据或复杂视图中可能会带来性能问题,尤其是在大型应用程序中需要处理大量数据时。
MVC
MVC(Model-View-Controller)模式是一种常见的软件设计模式,广泛应用于构建用户界面应用程序。它将应用程序分为三个核心部分:Model(模型)、View(视图) 和 Controller(控制器),并通过这三部分之间的交互来实现业务逻辑的分离和界面的更新。
组成部分
Model
模型是应用程序的核心,负责数据的存储、处理和业务逻辑。它不关心如何显示数据或如何响应用户输入,只关注数据的表示和操作,它负责获取、更新、删除数据,进行数据验证、业务规则的执行等操作。通常,它与数据库或其他持久化存储交互。
View
视图是用户界面的呈现部分,负责展示数据。它根据模型的状态来更新和显示内容。视图可以是网页、桌面窗口、移动应用界面等。它负责接收来自用户的输入(如点击、输入等),并将这些输入传递给控制器。它仅关注如何将模型数据转换为用户可视化的界面。
视图通过监听模型的变化(如数据更新)来更新显示内容。它通常会从控制器获取用户的交互事件。
Controller
控制器是模型和视图之间的中介,处理用户输入并调用相应的模型方法来更新数据。控制器接收用户的输入(如点击、键盘输入),处理这些输入后,再决定更新哪个模型或视图。控制器将用户的动作转换为对模型的操作,然后根据模型的状态来决定视图的更新方式。
工作原理
- 用户行为:用户通过视图与系统进行交互(例如点击按钮或输入文本)。
- 控制器接收输入:控制器接收到视图的用户输入(如按钮点击事件),然后处理该输入。
- 更新模型:控制器将数据传递给模型,模型执行相应的业务逻辑(如保存数据、更新数据等)。
- 通知视图:模型执行完业务逻辑后,通知视图进行更新。
- 视图更新:视图获取更新后的模型数据,并呈现新的界面给用户。
优点
分离关注点
MVC 模式通过将应用程序的不同部分(数据、逻辑和展示)分开,有助于保持代码清晰、可维护,且更易于扩展。
可复用性
由于模型和视图是解耦的,模型和视图可以在不同的场景下重用。例如,多个不同的视图可以共享相同的模型。
缺点
复杂性
对于简单的应用,MVC 可能会显得过于复杂,增加了代码的冗余。
数据更新延迟
如果视图更新依赖于模型的变化,可能会增加一定的延迟,尤其是当模型和视图之间的通信复杂时。
对比
数据绑定和交互方式的不同
MVC 的交互
在 MVC 模式中,View 和 Controller 是直接交互的。当用户触发视图上的操作(如点击按钮或输入内容),控制器处理这些操作,然后修改模型,最后视图会根据新的模型数据来重新渲染。Controller 起到了数据更新和用户交互的桥梁作用。控制器需要主动去更新视图。
MVVM 的交互
MVVM 的一大特点是它支持「双向数据绑定」。在 MVVM 模式中,ViewModel 和 View 之间通过数据绑定机制(通常是框架内建的绑定机制)连接。当 Model 更新时,ViewModel 自动更新,View 会根据绑定的 ViewModel 更新数据,而不需要手动触发。
Controller vs ViewModel
MVC 中的 Controller
控制器是处理所有用户交互逻辑的地方。每当用户与界面交互时,控制器会根据输入做出反应,并决定如何更新模型或视图。
控制器通常会直接操作视图,更新模型数据。
控制器需要手动更新视图,并且它会包含一些交互逻辑
MVVM 中的 ViewModel
ViewModel 是 MVVM 的核心。它的职责是准备视图所需的数据,并且与 View 保持双向绑定关系,通常不直接操作视图。
ViewModel 是对 View 的抽象,它向视图暴露数据,同时响应视图的变化(比如用户输入)。ViewModel 和 View 通过 数据绑定 自动保持同步。
应用场景
MVC
MVC 模式适用于中小型应用,尤其是传统的 Web 应用(例如基于服务器端渲染的应用),但是 react 使用这种模式。在 MVC 中,视图和控制器之间的紧密耦合使得其适合处理少量的交互和较为简单的界面。
MVVM
MVVM 模式特别适合现代前端开发,尤其是复杂的用户界面和交互(如单页面应用 SPA)。它利用 数据绑定 和 双向绑定 来简化代码,使得界面更新更加直观。
现代框架如 Vue、Angular 和 Knockout.js 都采用了 MVVM 模式,并且提供了强大的数据绑定和组件化机制来减少开发者的手动操作。
数据流
MVC 数据流
用户操作 → 视图 → 控制器 → 更新模型 → 视图更新
MVVM 数据流
用户操作 → 视图 → 视图模型(ViewModel) → 更新模型 → 视图更新(自动反应变化)
数据绑定
MVC 数据绑定
MVC 没有内建的自动数据绑定。视图和模型之间的同步通常是手动实现的,通常需要控制器来协调。
MVVM 数据绑定
MVVM 支持 双向数据绑定,即视图的变化会自动更新 ViewModel,ViewModel 的数据变化会自动更新视图。这样大大减少了视图和模型之间的手动同步。
总结
MVC 和 MVVM 都是软件设计模式,它们都用于分离应用程序的逻辑、视图和数据。
特性 | MVC | MVVM |
---|---|---|
核心部分 | Model、View、Controller | Model、View、ViewModel |
数据绑定 | 无数据绑定,手动更新视图 | 自动双向数据绑定 |
视图与控制器关系 | 视图通过控制器更新模型,控制器负责更新视图 | 视图通过 ViewModel 直接绑定数据 |
用户交互处理 | 控制器处理用户交互,手动更新视图 | ViewModel 处理用户交互,自动更新视图 |
适用场景 | 传统 Web 应用,简单交互 | 复杂的用户界面和交互,SPA,现代前端开发 |
后话
React 并不严格遵循某个经典的设计模式,比如 MVC 或 MVVM。相反,React 推崇组件化和单向数据流,以一种更灵活的方式实现了 UI 和数据的分离。它可以说是一种视图库,专注于视图层的构建,并通过其独特的组件模型和数据流来组织应用程序结构。
单向数据流
React 的数据流是单向的,这意味着数据在应用中始终沿着一个方向流动,即从父组件到子组件的单向数据流。父组件的状态变化会触发子组件的重新渲染,但反过来不会。这种单向数据流确保了数据的可追踪性和可预测性,这使得 React 非常适合于构建大型的、复杂的应用程序。
组件化
React 强调组件化,将 UI 划分为多个独立的、可重用的组件,每个组件都负责其独立的逻辑和视图结构。组件化设计可以使开发者将复杂的 UI 分解成小块,并在不同的地方复用相同的代码,增加代码的可维护性和复用性。
声明式编程风格
React 使用声明式编程,开发者只需指定界面在某种状态下应该是什么样的,而不是直接操作 DOM。React 负责管理视图的更新和渲染,这种方式比手动 DOM 操作更加高效且不易出错。
函数式编程
React 在设计中引入了很多函数式编程的思想,鼓励使用无副作用的纯函数(JSX)来处理 UI 逻辑。例如,React 组件通常是无状态的函数组件(尤其在 React Hooks 出现后),而状态管理则通过 useState、useReducer 等 Hook 来完成,使得代码更为简洁且易于调试。
类 MVVM 模式中的 “VM” 层
虽然 React 不完全是 MVVM,但在一定程度上有类似的 ViewModel 层的角色。例如,React 的组件状态(state)和 props 可以视为「ViewModel」,它们将数据和状态转换为适合 UI 展示的形式。React 中的数据流和状态管理方式有些类似 MVVM 中的 ViewModel 层的概念,但没有明确的双向绑定。React 只通过状态的变更重新渲染界面,保持单向数据流和组件间的独立性。