什么是MVVM?MVVM是Model-View-ViewModel的缩写。

要编写可维护的前端代码绝非易事。我们已经用MVC模式通过koa实现了后端数据、模板页面和控制器的分离,但是,对于前端来说,还不够。

这里有童鞋会问,不是讲Node后端开发吗?怎么又回到前端开发了?

对于一个全栈开发工程师来说,懂前端才会开发出更好的后端程序(不懂前端的后端工程师会设计出非常难用的API),懂后端才会开发出更好的前端程序。程序设计的基本思想在前后端都是通用的,两者并无本质的区别。这和“不想当厨子的裁缝不是好司机”是一个道理。

当我们用Node.js有了一整套后端开发模型后,我们对前端开发也会有新的认识。由于前端开发混合了HTML、CSS和JavaScript,而且页面众多,所以,代码的组织和维护难度其实更加复杂,这就是MVVM出现的原因。

在了解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模型应运而生。

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

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

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

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

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

<!-- HTML -->
<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的繁琐步骤中解脱出来!