HTML5 时代来临,使用原生代码编写 Native App 还是使用 HTML5 技术构建 Web App 的争论还未尘埃落定。但不可否认的是,Web 前端正变得越来越重,处理的逻辑越来越复杂。不仅仅是交互体验需要编写大量 javascript 代码,甚至很多业务逻辑都被搬到了前端。例如有些 购物车 的设计,无需向服务器端发送请求即可向购物车中添加商品并立刻计算出总价格。Web 的功能越来越强,javascript 代码越来越多,如何组织这大量的 js 成为亟待解决的问题。

一、前端 MVC

MVC 是一个不错的方案,它崇尚代码分离以及有效的分工模式。在传统的 Web 后端开发过程中,Models 是数据对象,它实现了对数据源的增删改查,并且大部分人把业务逻辑也放在这里;View 是用户界面,将处理过的数据结果呈现给用户;Controller 用来转发请求连接这两者。

想象一下,我们把这套行为准则搬到前端。

Ajax 在 Web 2.0 时代被广泛地使用,其中一个重要的基础是 JSON。JSON 已经成为最流行的数据交换格式,各大网站的 API 几乎都支持它。它独立于语言,相比 XML 它更加轻量。实际上 JSON 格式的数据就是一个 javascript 对象,所以我们经常看到前端通过 Ajax 获取 JSON 格式的数据然后显示在网页上。

前端实现的精美界面,后端仅提供 JSON 格式数据

前端实现的精美界面,后端仅提供 JSON 格式数据

于是在前端眼里,后端便退化为了数据源,实现完全的接口化。这种做法实际上是把后端的 View 剔除了,后端只负责输出 JSON 数据给前端,前端收到后将 JSON 对象当作 Model 根据业务需要进行各种操作。这样一个显而易见的好处就是跨平台,除了 Web 页面以及各种 App,第三方客户端也能通过开放的接口获取数据,形成良好的生态系统。

二、RESTful 接口

这就要求后端完全面向 API 设计,即使是 Web 版,也调用标准 OpenAPI 接口实现。REST 是目前大量 Web 2.0 网站使用的一种架构方案,同时它也是一种把世间一切看作无状态对象的思维模式。Web 上所有的东西(页面、图像等)本质上都是资源。一个 RESTful 的应用,包含了 GET、POST、PUT、DELETE 这四种 HTTP Verb 标准定义的语义操作,直接对应了后端对数据的增删改查。

GET    /articles       列表
GET    /articles/123   查看
POST   /articles       创建
PUT    /articles/123   更新
DELETE /articles/123   删除

在这种模式下,如何设计资源的 URL 就显得很重要了。RESTful 的世界观认为 URL 中不需要动词,因为所有的动作都可以抽象为 GET、POST、PUT、DELETE。简单的就是美的,Keep the simple things simple。优秀的 API 通常还会有一些通用的约定,例如使用复数名词,把复杂的参数放于 ? 号后面。更多关于 REST 的细节,强烈推荐来自 Apigee 的这篇演示文档《RESTful API Design》

还有几个知识点需要注意的——

  • GET 操作是安全的。所谓安全是指不管进行多少次操作,资源的状态都不会改变。比如我用 GET 浏览文章,不管浏览多少次,那篇文章还在那,没有变化。
  • PUT、DELETE 操作是幂等的。所谓幂等是指不管进行多少次操作,结果都一样。比如我用 PUT 修改一篇文章,然后在做同样的操作,每次操作后的结果并没有不同,DELETE 也是一样。
  • POST 操作既不是安全的,也不是幂等的,比如常见的POST重复加载问题:当我们多次发出同样的POST请求后,其结果是创建出了若干的资源。

在 PHP 中,可以使用全局变量 $_SERVER['REQUEST_METHOD'] 判断请求的方法,而且通常情况下都是用 file_get_contents('php://input') 输入流 来获取用户输入,不再使用 $_GET$_POST。各大 PHP 框架都对此进行了封装以支援 RESTful 风格的 API 开发,例如在 Yii Framework 中,我们可以这样定义路由

'urlManager'=>array(
	array('<controller>/index','pattern'=>'api/<controller>','verb'=>'GET','urlSuffix'=>'.json'),
	array('<controller>/show','pattern'=>'api/<controller>/<id\d+>','verb'=>'GET','urlSuffix'=>'.json'),
	array('<controller>/create','pattern'=>'api/<controller>','verb'=>'POST'),
	array('<controller>/update','pattern'=>'api/<controller>/<id\d+>','verb'=>'PUT'),
	array('<controller>/delete','pattern'=>'api/<controller>/<id\d+>','verb'=>'DELETE'),
),

三、前端 MVC 框架

前面已经说到,前端获取 JSON 数据作为 Model,那么 MVC 中的 View 和 Controller 又各自在前端扮演什么角色呢?这就需要一套完整的框架作为解决方案了。现在市面上流行的前端 MVC 框架大概有下面几种

  • Backbone.js. 应该是比较早出现的一个框架了,风靡于 Ruby on Rails 社区,包括 LinkedIn, Hulu, WordPress.com, Foursquare, Bitbucket, Disqus, Groupon, Basecamp, Pandora 以及国内的豆瓣,雪球,很多网站都使用了它,加上有 37signal 的背景,可谓是颇有知名度。它比较轻量,性能不错,但正因为如此导致它缺失了很多前端需要的重要组件,你必须自己写很多代码来实现,随着应用越来越复杂,要在更深层次嵌入视图,恶梦 开始了……另外我对它主页的文档展现形式有点不爽,一个脑袋里从来没有前端 MVC 概念的人去读,完全不知所云。
  • Knockout. 一个很强大的 MVVM 框架,如果你是 .NET 开发者你会很熟悉这种做法。它实现了 DOM 元素和 JS model 之间的双向绑定,也就是说你在表单中的每一个输入,都会实时更新到 model 里,最后提交表单也就等同于更新 model 的动作,一切很顺畅。官方主页上的例子也很容易理解。
  • AngularJS. 来自 Google,项目主页直接就是 demo,让你直接快速上手大呼过瘾。乍一看让人觉得很简单,不过进了门之后你会发现后面的路还很长。它制造了很多概念,对于前端来讲可以说是醍醐灌顶,比如它把 unix 里的 | (管道)搬到了前端……一开始我发现 AngularJS 和 Knockout 一样在 HTML 中写私有属性立即就否决了它,后来发现原来它也支持 HTML5 标准的 data-* 写法,于是又重新爱上了它。如果你习惯 Twitter Bootstrap 的应用方式我想你也会喜欢它的。
  • Ember.js. 比较有前途的一个框架,模板很好用,开发效率首屈一指。它有很多强制性约束,可以帮你自动完成很多事情。相比 Angular 上手可能要难一点,Angular 一开始不需要这么费劲也能做一些让人兴奋不已的事儿,而 Ember 缺少这种前期兴奋点。另外最令人不爽的是 Ember 的体积,min+gzip 压缩之后还有 55K,太庞大了。
  • Batman.js. 适合 RoR 开发,没怎么用过。看上去和 Knockout.js 很像,提供了自动生成代码的一些工具。整个库压缩后 40K 也比较大。
  • CanJS. 也没用过,社区规模小。
  • Spine.js. 没用过。轻量级。

那么,到底该选哪个框架呢?我的建议是选一个项目,去看一下各个框架的实现,谁的代码量少,谁的结构清晰,谁的实现方式让你感觉比较爽,自然就有答案了。这不,在 Github 上早已经有国际友人分别用各种框架实现了经典的 todo 应用,方便大家比较。就我个人而言,我选择了 AngularJS,用 AngularJS 代替 Backbone,代码减少了一半。虽然性能可能有点问题,但面向未来嘛,浏览器会越来越强大的。图灵社区 有一篇各种框架的详细比较,推荐一读。

四、AngularJS

AngularJS 的社区很活跃,开发迭代的速度也很快,毕竟是来自 Google,他们家著名的 CDN 都有收录 AngularJS。在 AngularJS 中原生提供了 RESTful 的操作接口,调用形式和 jQuery 的 ajax 相关 API 类似

$http.get('/someUrl').success(successCallback);
$http.post('/someUrl', data).success(successCallback);

完整的调用方式列表包括——

  • $http.get
  • $http.head
  • $http.post
  • $http.put
  • $http.delete

这样就能直接从浏览器发送标准的 HTTP 头,配合后端的 RESTful 接口,非常方便。

在刚刚过去的 2013 Google I/O 大会上,AngularJS 的开发者对框架做了 介绍和演示。目前 AngularJS 的文档可能还不够完美,你可能会多花一点时间。但是相信我,这是值得的。当你熟悉了整个框架的设计,开发效率会有质的飞跃。

另外还有一些暗坑,例如通过 AngularJS 以外的 js 代码(eg.第三方 jQuery 插件)对 AngularJS 的 model 进行操作(更改表单 field 值),需要手动调用 $apply 才能将新的值真正赋予 model,或者需要单独写 directive 来实现自动绑定。我另外写了一篇文章介绍我使用 AngularJS 的心得

后记

我现在深受 New Twitter 的影响,倾向于把尽可能多的逻辑放到客户端去做。Twitter 将 http://twitter.com/username 格式的 URL 统统都改成了 http://twitter.com/#!/username,因为浏览器的 HTTP 请求中并不包括 # 后面的内容,由此得知用户内容都是 Ajax 方式生成的,页面需要进行两次抓取,第一次是 html 网页(模板),第二次才是用户的发言内容。Web 网页和各种 app 客户端实现了共享同一个 RESTful 接口,这正是我所推崇的方式。在前端 MVC 的帮助下,数据和 UI 双向绑定,对页面元素的操作也更为便捷了。

参考链接: