随着响应式网页设计成为网页设计的主流方向,在网页设计领域出现了单页应用(Single-Page Application,简称 SPA),并在最近几年间好好的火了一把。随着 SPA 浪潮的席卷,出现了 MVC 架构模式的 BackboneJS,MVVM 架构模式的 AngularJS、Vue。其中 MVVM 是从 MVC 演进而来的,MVC 和 MVVM 可以统称为 MV*。

MVC/MVVM

MVC 架构模式将数据和视图进行分离,中间通过控制器(Controller)管理业务逻辑和用户的交互。

Flux 系列三:Flux VS MV* - 图1

MVC 中的 3 个角色

上图就是 MVC 的架构模式图,不难看出 MVC 架构由三个重要的部分组成:View、Model、Controller。它们在 MVC 架构模式中分别担任着什么样的角色和起着什么样的作用,下面我们将一一进行介绍。

Model

Model 俗称数据模型,在 MVC 架构模式中主要用来保存与业务逻辑相关的数据,主要和后端交互同步应用的数据,或者对数据进行校验。

注意:Model 并不会涉及到用户界面,也不会涉及表示层,只是存储着应用程序可能需要的独特形式的数据。但是当 Model 改变的时候,它会通知到它的观察者作出相应的反应。

View

View 层也就是我们说的视图层,是 Model 可视化的表现形式。前端的 View 层主要负责构建和维护 DOM 元素。相应的在应用程序中 View 对 Model 和 Controller 的了解也是有限的。用户可以和 View 进行交互,包括对 Model 进行数据的读取和编辑, 但是实际上更新 Model 的任务是在 Controller 层进行的。

一个 View 通常对应着一个 Model,当 Model 更改后进行通知,使 View 进行相应的界面更新。但是在实际的项目开发中,多个 View 对应多个 Model 也是很常见的。

在前端领域,View 对应的是 JavaScript 模板语言,它用于将 View 定义为包含模板变量的标记,使用变量语法,接受 JSON 格式的数据。

Controller

Controller 作为核心的控制层,主要负责连接 View 和 Model,一旦 Model 中有任何的数据变更都会应用到 View,而 View 的操作也会通过 Controller 应用到 Model 。在前端领域,Controller 的设计和传统的 MVC 中的概念还是存在着一定的区别。如 BackboneJS ,包含了 Model 和 View,但是它并没有 Controller,而其中 View 和路由的行为与 Controller 有点类似,但是他们实际上并不是 Controller。

MVC 的演进 —— MVVM

相对于 MVC ,MVVM 拥有 Model、View、ViewModel 三层,将 MVC 中的 Controller 层替换为了 ViewModel 层。在 MVVM 中,View 层数据状态的变化可以直接影响到 ViewModel 层,反过来 ViewModel 层的发生的动作改变了数据状态同样会影响到 View 层,这就是我们常说的数据双向绑定。这就是 Angular 和 Vue 的核心特色之一了。

Flux 系列三:Flux VS MV* - 图2

MVC 的问题

Flux 系列三:Flux VS MV* - 图3
在之前的内容中,有提到在实际项目中多个 View 对应多个 Model 也是很常见的。很显然,这样导致问题就是数据流动的方式特别混乱。

在前端领域的 MVC 设计中,Model 层会想外暴露出数据的 Set 和 数据变化的监听方法,导致 View 层可以随意的更改 Model 中的数据,同样也可以随意的监听 Model 数据的变化。这样的设计到最后的结果就是:当有一个庞大的 Model 数据发生变化时,可能会触发无数的 change 事件。在这些 change 的回调函数里面可能还有新的 Set 逻辑被调用,这样可能会导致更多的 change 事件被触发。

更糟糕的是,Model 和 Model 之间也可能存在一定的联系,当一个 Model 的数据变化时可能会改变另一个 Model 中的数据,整个应用的数据流变得乱七八糟,不可预测。如果是在开发或者生产中出现了问题,调试会变成一件特别恐怖的事情,或者压根就不知道从何入手。如此发展下去只会越变越糟糕。

特别是对象数据的增、删、改来说,MVC 为了提高性能,需要在 View 层构建大量的渲染处理函数,以实现视图的局部更新。但这会导致更新的逻辑变得异常复杂,定位问题的难度会大大增加。

解决方案

如果渲染函数只有一个且存在于 Controller 层,每次 Model 数据更新后,就重新渲染页面。这样的话,每当有数据发生变化时,只需要调用 Controller 层的重渲染函数就可以了,保持了数据和当前页面状态的唯一确定性,保证了数据流的清晰。然而重渲染会严重影响性能,这直接关系到用户的体验。

在解决这类问题方面,MVC 陷入了两难的困境,于是催生了 MVVM 和状态管理工具的出现。