MVC 架构

随着现代浏览器功能越来越强大,特性越来越丰富,用 JavaScript 建立成熟的网络应用不仅仅是可行的,也越来越受到欢迎。根据 HTTP Archive趋势,已部署的 JavaScript 代码大小在一年内增长了 45%。

JS transfer size and JS requests

随着 JavaScript 越来越流行,我们的客户端应用程序也比以前要复杂得多。应用程序开发需要许多开发者的合作,编写可维护可重用的代码在新的网络应用时代显得至关重要,包含丰富客户端特性的 Chrome 应用也不例外。

设计模式对于编写可维护、可重用代码是十分重要的。模式是一种可重用的解决方案,适用于软件设计中经常遇到的问题——在我们的情况中即编写 Chrome 应用。我们建议开发者将应用分解为一系列互相独立的组件,遵循 MVC 模式。

最近几年,一系列 JavaScript 的 MVC 框架已经开发出来,例如 backbone.jsember.jsAngularJSSenchaKendo UI 以及更多。尽管它们都有自己独特的优势,它们中的每一个都遵循某种形式的 MVC 模式,并且以鼓励开发者编写结构更清晰的 JavaScript 代码为目标。

MVC 模式概述

MVC 在标准 JavaScript 之上提供了架构上的优势——它帮助您编写更有条理,因而更容易维护的代码。这一模式已经在多种语言及几代程序员下使用并经过广泛的测试。

MVC 包含三个组成部分:

model-view-controller

模型(Model)

模型是应用程序数据对象存储的位置。模型并不了解有关视图与控制器的任何细节。当模型更改时,通常它会通知其监听者更改已发生。

要进一步理解它,让我们使用待办事项列表应用作为例子,它是一个简单的单页面应用,可以跟踪您的任务列表。


model-view-controller

这里模型就代表和每一件待办事项相关联的属性,例如描述与状态。当新的待办事项创建时,它将在模型的一个实例中储存。

视图(View)

视图就是展现给用户的内容以及用户与应用交互的方式。视图使用 HTML、CSS、JavaScript 实现,经常还会用到模板。您的 Chrome 应用的这一部分能够访问 DOM。

例如,在以上的待办事项网络应用中,您可以创建一个视图,漂亮地向您的用户展现待办事项列表。用户也可以通过某些输入格式键入新的待办事项,然而视图并不知道如何更新模型,因为这是控制器的工作。

控制器(Controller)

控制器用来作出决定,同时也是模型与视图之间的桥梁。当模型更改时控制器更新视图,同时它向视图添加事件监听器,当用户操纵视图时更新模型。

在待办事项网络应用中,当用户选中项目将它标记为已完成时,单击事件将转发至控制器。控制器修改模型,将项目标记为已完成。如果数据需要持久保存,它将同时向服务器发出一个异步保存的请求。在富客户端网络应用开发,例如 Chrome 应用中,保持数据在本地持久存储也是至关重要的。在这种情况下,控制器也要进行处理,将数据保存到客户端存储区,例如文件系统 API

有一些 MVC 设计模式的变体,例如 MVP(模型——视图——呈现器)及 MVVP(模型——视图——视图模型)。即使是所谓的 MVC 设计模式本身,在传统的 MVC 模式与各种编程语言中的现代解释之间也有一些变化。例如,某些基于 MVC 的框架会让视图监听模型中的更改,但是另一些会让控制器处理视图更新。该文章的重点不在比较各种实现,而是在于关注点分离及其对于编写现代网络应用的重要性。

如果您有兴趣了解更多,我们推荐 Addy Osmani 的在线图书:学习 JavaScript 设计模式(英文)。

综上所述,MVC 模式给应用开发者带来了模块化并使以下几项成为可能:

  • 可重用与可扩展的代码
  • 事务逻辑与视图逻辑的分离
  • 允许负责不同组件(例如用户界面层与核心逻辑)的开发者同时工作。
  • 更容易维护。

MVC 持久存储模式

有许多不同的方式通过 MVC 框架实现持久存储,每一种都有不同的优缺点。当您编写 Chrome 应用时,选择您觉得最自然并且最适合您的应用需要的 MVC 与持久模式框架。

模型自己进行持久存储——ActiveRecord 模式

在诸如 Ruby on Rails 的服务器端框架以及诸如 Backbone.js ember.js 之类的客户端框架中 ActiveRecord 模式都很流行,它将持久存储的责任放在了模型自身上,通常通过 JSON API 实现。

与让模型处理持久存储有所区别的方式是引入单独的存储区与适配器 API 的概念。存储区、模型与适配器(在某些框架中称为代理)同时工作。存储区是包含已加载模型的库,并提供诸如创建、查询及过滤包含在其中的模型实例的功能。

适配器或代理从存储区接收请求,并将它转换为对您的持久数据层(例如 JSON API)进行的适当操作。这种方式在现代网络应用设计中十分有意义,因为您经常与不止一个持久数据层交互,例如远程服务器与浏览器的本地存储区。Chrome 应用为客户端存储提供了 Chrome 浏览器的存储 APIHTML5 文件系统 API

优势:

  • 使用简单,易于理解。

缺点:

  • 测试困难,因为持久存储层混合在对象架构内。
  • 让不同的对象使用不同的持久存储区比较困难(例如文件系统 API、IndexedDB 与服务器端)。
  • 在其他应用程序中重用模型可能会带来冲突,例如在两个不同的视图中共享一个顾客类,每一个视图都希望保存到不同的位置。

控制器进行持久存储

在这种模式中,控制器包含对模型与数据存储区的引用,并负责保持模型持久存储。控制器对诸如加载、保存、删除之类的生命周期事件作出响应,并向数据存储区发送命令以便获取或更新模型。

优势:

  • 方便测试,可以向控制器传递一个伪数据存储区写入测试内容。
  • 同样的模型可以与多个数据存储区重用,只要构造具有不同数据存储区的控制器。

缺点:

  • 代码维护起来可能会更复杂。

应用控制器进行持久存储

在某些模式中,有一个超级控制器,负责在一个 MVC 与另一个之间导航。例如应用控制器决定“后退”按钮将客户端从编辑屏幕(包含 MVC 小工具/格式)移动至设置屏幕。

在应用控制器模式中,应用控制器响应事件,并通过向数据存储区发出调用,加载所需的模型并构造所有匹配视图及控制器的方式更改应用的当前屏幕。

优势:

  • 将持久存储层移到更高的层次,这样更容易修改。
  • 不会污染较低级的控制器,例如日期选择器控制器,使它需要了解持久存储。

缺点:

  • 应用程序的每一个“页面/屏幕”现在都需要编写或更新大量公共代码:模型、视图、控制器、应用程序控制器。

MVC 对于设计 Chrome 应用是至关重要的。我们推荐如下遵循 CSP(内容安全策略)的 MVC 框架,用于编写安全而且可伸缩的 Chrome 应用:

有用的资源

在线资源

书籍