# 16 | 百花齐放,百家争鸣:前端MVC框架 你好,我是四火。 我在上一章讲到了 MVC 的原理,今天我来讲讲前端的 MVC 框架。这部分发展很快,它们比后端 MVC 框架出现得更晚,但是社区普遍更活跃。 我们在学习的过程中,需要继续保持深度和广度的均衡,既要对自己熟悉的那一款框架做深入了解,知道它的核心特性,明白其基本实现原理,对于其优劣有自己的想法;也要多了解了解这个技术的百花园,看看别的框架是什么,想想有什么优势和缺点,拓宽视野,为自己能够做出合理的技术选型而打下扎实的基础。 ## 前端 MVC 的变革 让我们来回想一下,在 [\[第 07 讲\]](https://time.geekbang.org/column/article/140196) 中,介绍过的 MVC 架构。实际上,我们可以把前端的部分大致归纳到视图层内,可它本身,却还可以按照 MVC 的基本思想继续划分。这个划分,有些遵循着 MVC 两个常见形式之一,有些则遵循着 MVC 的某种变体,比如 MVVM。 我们都知道前端技术的基础是 HTML、CSS 和 JavaScript,可随着技术的发展,它们在前端技术分层中的位置是不断变化的。 在前端技术发展的早期,Ajax 技术尚未被发明或引进,页面是一次性从服务端生成的,即便有视图层的解耦,页面聚合也是在服务端生成的(如果忘记了服务端的页面聚合,请回看 [\[第 09 讲\]](https://time.geekbang.org/column/article/141817))。也就是说,整个页面一旦生成,就可以认为是静态的了。 在这种情况下,如果单独把前端代码的组织也按照 MVC 架构来划分,你觉得 HTML 到底算模型层还是视图层? * 有人说 ,是模型层,因为它承载了具备业务含义的文本和图像等资源,是数据模型的载体,它们是前端的血和肉; * 有人说,是视图层,因为它决定了用户最后看到的样子,至于 CSS,它可以决定展示的“部分”效果,但却不是必须的(即便没有 CSS,页面一样可以展示)。 其实,这两种说法都部分正确。毕竟,如果采用服务端聚合,等浏览器收到了响应报文,从前端的角度来看,模型和呈现实际已经融合在一起了,很难分得清楚。 等到 Ajax 技术成熟,客户端聚合发展起来了,情况忽然就不一样了。代表视图的 HTML 模板和代表数据的 JSON 报文,分别依次抵达浏览器,JavaScript 再把数据和模板聚合起来展示,这时候这个过程的 MVC 分层就很清晰了。 曾经 jQuery 是最流行的 JavaScript 库,但是如今随着前端业务的复杂性剧增,一个单纯的库已经不能很好地解决问题,而框架开始扮演更重要的地位,比如大家常常耳闻的前端新三驾马车 Vue.js, Anuglar 和 React。 ## Angular 对于现代 MVC 框架的介绍,我将用两个框架来举例。前端框架那么多,希望你的学习不仅仅是知识的堆砌,而是可以领会一些有代表性的玩法,能有自己的解读。第一个是 Angular,我们来看看它的几个特性。 ### 1\. 双向绑定 曾经,写前端代码的时候,数据绑定都是用类似于 [jQuery](https://jquery.com/) 绑定的方式来完成的,但是,**有时候视图页面的数值变更和前端模型的数据变更,这两个变更所需的数据绑定是双向的,这就会引发非常啰嗦的状态同步:** * **数据对象发生变更以后,要及时更新 DOM 树;** * **用户操作改变 DOM 树以后,要回头更新数据对象。** 比方说,在 JavaScript 中有这样一个数据对象,一本书: ``` book = {name: "Steve Jobs Biography"} ``` 在 HTML 中有这样的 DOM 元素: ``` ``` 我们需要把数据绑定到这样的 DOM 对象上去,这样,在数据对象变更的时候,下面这两个 DOM 对象也会得到变更,从而保证一致性: ``` $("#book-input").val(book.name); $("#book-label").text(book.name); ``` 相应地,我们还需要些绑定语句来响应用户对 book-input 这个输入框的变更,同步到 book-label 和 JavaScript 的 book 对象上去: ``` $("#book-input").keydown(function(){ var data = $(this).val(); $("book-label").text(data); book.name = data; }); ``` 你可以想象,当这样的关联变更很多的时候,类似的样板代码该有多少,复杂度和恶心程度该有多高。 于是 Angular 跳出来说,让我们来使用双向绑定解决这个问题吧。无论我们“主动”改变模型层的业务对象(book 对象),还是视图层的这个业务对象的展示(input 标签),都可以自动完成模型层和视图层的同步。 实现方法呢,其实只有两步而已。首先模型层需要告知 DOM 受到哪个控制器控制,比如这里的 BookController,然后使用模板的方式来完成从模型到视图的绑定: ```