前端架构模式

429 阅读6分钟

MVC,MVP,MVVM是三种常见的前端架构模式(Architectural Pattern),它通过分离关注点来改进代码组织方式。不同于设计模式(Design Pattern),只是为了解决一类问题而总结出的抽象方法,一种架构模式往往能使用多种设计模式。

前端发展的历史

在了解MVVM之前,我们先回顾一下前端发展的历史。

在上个世纪的1989年,欧洲核子研究中心的物理学家Tim Berners-Lee发明了超文本标记语言(HyperText Markup Language),简称HTML, 并在1993年成为互联网草案。从此,互联网开始迅速商业化,诞生了一大批商业网站。

最早的HTML页面是完全静态的网页,它们是预先编写好的存放在Web服务器上的html文件。浏览器请求某个URL时,Web服务器把对应的html文件扔给浏览器,就可以显示html文件的内容了。

如果要针对不同的用户显示不同的页面,显然不可能给成千上万的用户准备好成千上万的不同的html文件,所以,服务器就需要针对不同的用户,动态生成不同的html文件。一个最直接的想法就是利用C、C++这些编程语言,直接向浏览器输出拼接后的字符串。这种技术被称为CGI:Common Gateway Interface。

很显然,像新浪首页这样的复杂的HTML是不可能通过拼字符串得到的。于是,人们又发现,其实拼字符串的时候,大多数字符串都是HTML片段,是不变的,变化的只有少数和用户相关的数据,所以,又出现了新的创建动态HTML的方式:ASP、JSP和PHP——分别由微软、SUN和开源社区开发。

在ASP中,一个asp文件就是一个HTML,但是,需要替换的变量用特殊的<%=var%>标记出来了,再配合循环、条件判断,创建动态HTML就比CGI要容易得多。

但是,一旦浏览器显示了一个HTML页面,要更新页面内容,唯一的方法就是重新向服务器获取一份新的HTML内容。如果浏览器想要自己修改HTML页面的内容,就需要等到1995年年底,JavaScript被引入到浏览器

有了JavaScript后,浏览器就可以运行JavaScript,然后,对页面进行一些修改。JavaScript还可以通过修改HTML的DOM结构和CSS来实现一些动画效果,而这些功能没法通过服务器完成,必须在浏览器实现。

用JavaScript在浏览器中操作HTML,经历了若干发展阶段:

第一阶段,直接用JavaScript操作DOM节点,使用浏览器提供的原生API:

var dom = document.getElementById('name');
dom.innerHTML = 'Homer';
dom.style.color = 'red';

第二阶段,由于原生API不好用,还要考虑浏览器兼容性,jQuery横空出世,以简洁的API迅速俘获了前端开发者的芳心:

$('#name').text('Homer').css('color', 'red');

第三阶段,MVC模式, 需要服务器端配合,JavaScript可以在前端修改服务器渲染后的数据。

第四阶段,现在,随着前端页面越来越复杂,用户对于交互性要求也越来越高,想要写出Gmail这样的页面,仅仅用jQuery是远远不够的。MVVM模型应运而生。

Web时代

Web1.0时代

在web1.0时代,并没有前端的概念。开发一个web应用多采用 ASP.NET/Java/PHP编写,项目通常由多个aspx/jsp/php文件构成,每个文件中同时包含了HTML、CSS、JavaScript、C#/Java/PHP代码,系统整体架构可能是这样子的:

image.png

这种架构的好处是简单快捷,但是,缺点也非常明显:JSP代码难以维护。(前后端代码都混在了一起)

为了让开发更加便捷,代码更易维护,前后端职责更清晰。便衍生出MVC开发模式和框架,前端展示以模板的形式出现。典型的框架就是Spring、Structs、Hibernate。整体框架如图所示:

image.png

使用这种分层架构,职责清晰,代码易维护。但这里的MVC仅限于后端,前后端形成了一定的分离,前端只完成了后端开发中的view层。

但是,这种模式存在的问题:

  • 前端页面开发效率不高
  • 前后端职责不清晰

Web2.0时代

自从Gmail的出现,ajax技术开始风靡全球。有了ajax之后,前后端的职责就更加清晰了。因为前端可以通过ajax与后端进行数据交互,因此,整体的架构图也变化成了下面:

image.png

通过ajax与后台服务器进行数据交互,前端开发人员只需要开发页面这部分内容,数据可由后台进行提供。而且ajax可以使得页面实现部分刷新,减少了服务端负载和流量消耗,用户体验也更佳。这时,才开始有专职的前端工程师。同时前端的类库也慢慢开始发展,最著名的就是jQuery了。

当然,此架构也存在问题:缺乏可行的开发模式承载更复杂的业务需求,页面内容都糅杂在一起,一旦应用规模增大,就会导致难以维护了。因此,前端的 MVC也随之而来。

前后端分离后的架构演变 - MVC/MVP/MVVM

MVC

前端的MVC与后端类似,具备着ViewControllerModel

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

  • Model(模型)表示应用程序核心(如数据库),程序编写程序应用的功能(实现算法等等)、数据库管理。负责保存应用数据,与后端数据进行同步,封装和处理数据。
  • View(视图)显示效果(HTML页面),界面设计人员进行图形界面设计。 负责视图展示(html,ftl,jsp...),将model中的数据可视化出来。
  • Controller(控制器)处理输入(业务逻辑),对请求进行处理,负责请求转发。 负责业务逻辑,根据用户行为对Model数据进行修改。专门来处理请求的:主要做3件事
    • 接收参数
    • 调用service层代码
    • 控制页面跳转

三者形成了一个如图所示的模型:

image.png

image.png

这主要是基于分层的目的,让彼此的职责分开View一般通过Controller来和Model进行联系。ControllerModelView的协调者,View和Model不直接联系。基本都是单向联系。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。

MVC模式的流程如下: 浏览器通过视图向控制器发出请求,控制器接收到请求之后通过选择模型进行处理,处理完请求以后再转发到视图,进行视图界面的渲染并做出最终响应。

在MVC模式中,视图View可以用JSP/HTML/CSS实现,模型Model可以用JavaBean实现,而控制器Controller就可以用Servlet来现。

这样的模型,在理论上是可行的。但往往在实际开发中,并不会这样操作。因为开发过程并不灵活。例如,一个小小的事件操作,都必须经过这样的一个流程,那么开发就不再便捷了。

在实际场景中,我们往往会看到另一种模式。如图:

image.png

这种模式在开发中更加的灵活,backbone.js框架就是这种模式。

但是,这种灵活可能导致严重的问题:

  • 数据流混乱。如下图:

image.png

  • view比较庞大,而Controller比较单薄:由于很多的开发者都会在view中写一些逻辑代码,逐渐的就导致view中的内容越来越庞大,而controller变得越来越单薄。

既然有缺陷,就会有变革。前端的变化中,似乎少了MVP的这种模式,是因为Angular早早地将MVVM框架模式带入了前端。MVP模式虽然前端开发并不常见,但是在安卓等原生开发中,开发者还是会考虑它。

MVP

MVPMVC很接近,P指的是Presenter,可以理解为一个中间人,它负责着ViewModel之间的数据流动,防止ViewModel之间直接交流。我们可以看一下图示:

image.png

我们可以看到,Presenter负责和Model进行双向交互,还和View进行双向交互,这种交互方式,相对于MVC来说少了一些灵活,View变成了被动视图,并且本身变得很小,虽然它分离了ViewModel。但是应用逐渐变大之后,导致presenter的体积增大,难以维护。要解决这个问题,或许可以从MVVM思想中找到答案。

在前端没有经典的MVP框架

MVVM

MVVM(Model-View-ViewModel)

MVVM是一种软件设计模式,这里要说一下设计模式,我们通常所的设计模式是指面向对象中的设计模式,用在面向对象编程语言中。但软件设计模式是更高一个级别的设计模式,两者不是同一个东西。

MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离。

把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

ViewModel如何编写?需要用JavaScript编写一个通用的ViewModel,这样,就可以复用整个MVVM模型了。

在MVVM框架下视图和模型是不能直接通信的,只能通过ViewModel进行交互,它能够监听到数据的变化,然后通知视图进行自动更新,而当用户操作视图时,VM也能监听到视图的变化,然后通知数据做相应改动,这实际上就实现了数据的双向绑定。并且V和VM可以进行通信。

image.png

ViewModel可以理解为在presenter基础上的进阶版。如图所示:

image.png

  • ViewModel 通过实现一套数据响应式机制自动响应Model中数据变化。
  • 同时ViewModel会实现一套更新策略自动将数据变化转换为视图更新。
  • 通过事件监听响应View中用户交互修改Model中数据。
  • 这样在ViewModel中就减少了大量DOM操作代码。
  • MVVM在保持View和Model松耦合的同时,还减少了维护他们关系的代码,使用户专注于业务逻辑,兼顾开发效率和可维护性。

MVVM本质上就是MVC的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

优点

  • 低耦合:View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  • 可重用性: 可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
  • 独立开发: 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面的设计。

一个MVVM框架和jQuery操作DOM相比有什么区别?

我们先看用jQuery实现的修改两个DOM节点的例子:

<p>Hello, <span id="name">Bart</span>!</p>
<p>You are <span id="age">12</span>.</p>

Hello, Bart!

You are 12.

用jQuery修改name和age节点的内容:

'use strict';
----
var name = 'Homer';
var age = 51;

$('#name').text(name);
$('#age').text(age);
----
// 执行代码并观察页面变化

如果我们使用MVVM框架来实现同样的功能,我们首先并不关心DOM的结构,而是关心数据如何存储。最简单的数据存储方式是使用JavaScript对象:

var person = {
    name: 'Bart',
    age: 12
};

我们把变量person看作Model,把HTML某些DOM节点看作View,并假定它们之间被关联起来了。

要把显示的name从Bart改为Homer,把显示的age从12改为51,我们并不操作DOM,而是直接修改JavaScript对象:

Hello, Bart!

You are 12.

'use strict';
----
person.name = 'Homer';
person.age = 51;
----
// 执行代码并观察页面变化

执行上面的代码,我们惊讶地发现,改变JavaScript对象的状态,会导致DOM结构作出对应的变化!

这让我们的关注点从如何操作DOM变成了如何更新JavaScript对象的状态,而操作JavaScript对象比DOM简单多了!

这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操作DOM的繁琐步骤中解脱出来!

总结

基础概念

  • 这三者都是框架模式,它们设计目标都是为了解决ModelView的耦合问题。
  • MVC模式出现较早主要应用在后端,如Spring MVC、ASP.NET MVC等,在前端领域的早期也有应用,如Backbone.js。它的优点是分层清晰,缺点就是数据流混乱,灵活性带来的维护性问题。
  • MVP模式是MVC的进化形式,Presenter作为中间层负责MV通信,解决了两者耦合问题,但P层过于臃肿会导致维护问题。
  • MVVM模式在前端领域有广泛应用,它不仅解决MV耦合问题,还同时解决了维护两者映射关系的大量代码和DOM操作代码,在提高开发效率、可读性同时还保持了优越的性能表现。

MVC VS MVVM

  • MVC中Controller演变成MVVM中的ViewModel
  • MVVM通过数据来显示视图层而不是节点操作
  • MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验

一、MVVM 是一种设计思想, M表示Model, V表示视图View, VM表示数据与模型 (区别MVC中, C是用于跳转至某个页面, 而VM只是将数据进行转换或者封装)

    ① 当前台View发生变化时,  View与VM进行了绑定, VM又与M进行交互, 从而使M得到了改变。

    ② 当M变化时, M通知VM, VM与V进行了绑定,然后实现M和V

二、MVC 是一种架构模式, M表示Model,   V表示视图View,   C表示控制器Controller

    ① Model 负责存储丶定义丶操作数据丶从数据库中获取数据;

    ② View 用来展示给用户, 并且和用户进行交互;

    ③ Controller 是 Model 和 View 的协调者, Controller 把 Model 中的数据拿过来给View使用。Controller 可以直接与Model和View进行通信, 而View不能Controller直接通信, 当有数据更新时,  Model 也要与 Controller进行通信 处理相关业务。

浅谈vue中的MVVM

image.png

MVVM是MVC改进版

  • model: 模型,数据访问层,js代码
  • view:视图层,html,css
  • viewModel:公共属性和命令,

mvvm没有控制器C,有一个绑定器,视图和模型之间进行通信的。

将结构布局和业务逻辑分开,通过viewmodel在结构布局和业务逻辑进行通信

优势:

  1. 低耦合,视图层和业务逻辑层分开,耦合度降低
  2. 可重用性高
  3. 分层开发,便于维护