bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式。通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序一样。特别是在当今的移动时代,单页程序如果放在移动设备上去浏览就能够拥有像native app一样的体验,也许我们web开发者们应该期待这种技术的大力普及,这样不管前端还是后端都是我们的天下啊,让那些Andrioid和IOS开发者们追赶我们吧!好吧,废话不说了,我们会从0开始搭建这样一个单页的web站点,并且会向大家展示我们标题所列的这些开源框架是如何帮助我们快速构建的。新技术比较多,我也是学习,有不足的地方请海涵 :)
注:由于这个Demo是要给国外的同事看的,所以页面内容显示是英文的,请见谅。戳这里看线上Demo。http://myspademo.cloudapp.net
源码地址: https://github.com/jesselew/SpaDemo
目录
需求介绍
我们的需求很简单,通过这个单页程序完成对Event的管理,下面简单列几条需求。
功能性需求
- 添加修改Event
- Event 有opening和closed的状态,也就是需要有关闭Event的功能
- Event列表页可以根据状态过滤
- Closed的Event不能再进行修改
非功能性需求
- 尽可能的减少对服务器的请求
- 数据完整性(验证)
- 认证和授权(系统会有至少2种角色,并且拥有不同的权限)
- 可维护性
认证和授权这一块暂时没有做,后面可以继续完善,验证这一块只做了后端的,通常为了安全和用户体验是需要后端和前端都要实现验证的。这个Demo我已经上传到Windows Azure上去了,大家来体验一把。http://myspademo.cloudapp.net
单页程序介绍
首先我觉得可以把页面的响应模式分成这样大概3个阶段:
1. 最传统的阶段:什么都得刷新
最传统的web站点中,客户端向服务器发送请求,服务器响应之后把生成好的HTML通过Response返回给客户端,这样一来一往。体验当然是最不好的,同时对服务器来说也需要处理的更多。
2. 页面局部刷新
至从Ajax火起来之后,大家就想起了这一点。页面某一块局部的数据可以在页面在客户端加载完之后,再从新发起一个请求去把某一块的HTML代码再拿下来显示到页面中。这里面有两种做法,一种是后台直接把HTML生成好了直接返回,另一种做法是服务器只返回数据,客户端再拼出HTML。采取第二种做法的时候,有人可能已经用上了先进的模板技术,有人可能还在使用强大的字符串拼接技术。 不管怎么说,我们进步了,用户可以先看到页面,然后某一块慢慢加载,用户感觉爽了,再也不是一片空白在那里转啊转啊的了。
3. 整站单页
整站单页的时代到来最早是在2005年,当然那时候还只是一个术语。具体的例子,我最早接触到的是Gmail,当然最简单的单页其实很简单比如说某Q邮箱,整了个Frame在页面里面,不管你怎么点,它懒是感觉没有刷新呀。这里先简单说说我们要实现的这个单页和用Frame实现的单页相比有什么优势。
- 拥有良好定义的URL,对用户和搜索引擎都更友好。
- 可以实现衔接动画,这一点在移动设备上特别重要。
页面生命周期对比
.png)
这里从MSDN上面扒来了一张图,上面的传统的页面生命周期,下面是我们这种单页程序页面的生命周期。我们来看看这种模式的页面会为我们的用户和开发者带来哪些优势和难题。
优势
- 对于用户而言,更好的用户体验,特别体现在可移动端和可触摸设备上
- 对于开发都者而言,在定义了良好的分层架构之后,UI与数据可以完全分离,只要后台的数据接口不改变,后台的逻辑可以随意的改动页不影响前端展示,而在加上前端MVVM框架之后,我们前端的数据也可以与UI完成分离。
难题
- 最大的难题是Javascript部分,由于全部在一个页面,我们需要处理变量覆盖,变量作用域,对于前端开发人员来说要求会更上一层楼
- 对于全球化,授权等模块都需要重新考虑和设计以便更适合这种单页程序的开发
项目架构
扒了一张图之后,我的图就得画的跟它的协调,没有我的手写风格好看,有木有?

- 用Knockout作前端MVVM框架
- 用requireJS来加载远程模板
- 用director来作前端route
- model数据是直接和web api交互的,包括验证和授权
- 模板是一个Controller,每一个模板对应一个Action
View Container
这是一个客户端的模板容器,在requireJS的基础封装了一下,第一次调用某个模板的时候会去服务器上拿,后来就直接用客户端的了。
为什么模板不直接用html的?
这个问题我也想过,用纯html的就不必走mvc那一套生命周期了,服务器压力减少不小。但是考虑到我们view当中的授权模块和全球化资源,其实是可以直接在服务器端处理好再返回的。而且我也偷了一个懒,没有把这些放在客户去实现,大家有好的点子可以分享的么?
开源框架介绍
上面用了这么多的开源框架,那么它们都是干什么的,又是如何使用的呢? 这里我们就小小的来聊一聊这些开源的框架吧。
Bootstrap
这玩意我想很多人都知道,我就不多说了。有了它之后,我们程序员不需要美工也可以做出很漂亮的界面了,虽然我这个Demo没有很好看,但要是没有它那还真不知道要丑上多少倍。它还有中文版的站点: http://www.bootcss.com/
director.js
这是一个前端的route框架,什么叫前端route呢?大家如果去看我的那个Demo就会发现,URL并不是像某Q邮箱那样一直不变的,我们还是可以像以前那样每一个单一的功能一个URL。比如说:
- #/events/create
- #/events/all
- #/events/closed
- #/events/1
除了对用户比较友好之后,写代码的时候也会更加逻辑清晰,因为director会为每一个url绑定一个函数,就像mvc里面的action一样。当用户输入对应的url的时候,相应的函数就会被触发。
下面是来自官方首页的一个小小的例子,让你一眼就会用director。

requireJS
这玩意我也不用多介绍了吧,它具有延迟加载和避免重复加载的功能,来自官方的定义: requireJS是一个JavaScript文件和模块加载器。
knockout.js
这玩意就算我想给你介绍也不是三言两语就能说的清的,具体您还是参考源码吧。或者园子里面的大叔曾经翻译了官方的一个教程,有兴趣同学可以看看。 总之它是一个JavaScript的MVVM框架,当然这种框架有很多,backboneJS, breezeJS, Durandal,EmberJS,Angular 等等,我并没有全部了解过,所以我也不能告诉你他们的优势和缺点分别在哪里。选择knockout.js是因为之前了解过,好上手,然后以上这3种开源的框架全是基于MIT开源协议的,这样我们就可以用它做商业开发了。
用requireJS实现远程模板的调用
直接用require来加载html模板是不行的,人家已经说了是一个Javascript文件和模块的加载器。所以这里面我们需要用到requireJS的文本插件,这样我们就可以用它来加载文本了。https://github.com/requirejs/text
把那个text.js下载下来,直接放到我们程序的根目录下,然后我们就可以用像加载js一样的方法来加载html代码了,除了要在我们文件位置前面加上一个text! 之外。
require(['text!/template/createevent'], function (template) {
// 你在这里就可以拿到模板了。
})
rest中关于局部更新的讨论
我们常用的http verb有四种:

我们用PUT方式去更新的话,是将整个Model全部更新。当然你也可以换成下面这种方式,只更新你想要更新的字段。
[HttpPut]
public void Put(Event item)
{
var newItem = new Event();
newItem.Id = item.Id; // 在下面将你想要更新的值转到newItem下
newItem.Title = item.Title; if (!repository.Update(newItem))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
注意:Put方式的URl只有一种(在我们不建其它route的情况下),也就是我们上面列出来的 /api/events/{id},然后将event对象作为body传过去。比如说在我们的demo中,我们有更新操作,还有像“关闭”这样的操作,我想这样的操作几乎在每一个系统里面都会遇到,这样的操作只会更新一个字段(在这里是“状态”列)。 那我怎么样再建一个Put方法去更改这一个字段呢?而且最好的方法是我只用传id过去就可以了。
通过google,我找到一个叫Patch的玩意, 它也是一种http verb,并且同样也是提供更新操作。但是与Put不一样的是Patch允许只将你需要更改的字段传到服务器端。
var obj = { Revision : "2"};
$.ajax({
url: 'api/values/1',
type: 'PATCH',
data: JSON.stringify(obj),
dataType: 'json',
contentType: 'application/json',
success: function (data) {
}
});
但是不管怎么说,这种方式我是没有行通的,一旦我的实体对象加上一些验证的Attribute比如说Required之后,那些字段全都会被赋上默认值。 最后我不得不放弃了这种做法。
添加Route来创建两个PUT方法
另外一种做法,也就是我们Demo中实现的做法是增加了一个Route,在我们的web api中实现了两个put的方法。
[Route("api/events/{id}/close")]
public void Put(int id)
{
var item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
item.Status = EventStatus.Closed;
if (!repository.Update(item))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
这样当我用PUT的方式提交到 api/events/3/close 的时候,我们的web api就会执行上面的方法然后把我们的event关闭了。
WEB API的验证
基本上任何系统都避免不了与验证打交道,除非那个系统压根不从用户那里获取数据。WEB API的验证方式大至相同,我们仍旧可以在我们的Model中采用Attribute的方式去声明验证条件。
public class Event
{
public int Id { get; set; }
[Required]
[MinLength(10)]
public string Title { get; set; }
public string Description { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
[Required]
public string Owner { get; set; }
public EventStatus Status { get; set; }
}
在api方法中我们用ModelState.IsValid判断就可以了。
public HttpResponseMessage Post(Event item)
{
if (ModelState.IsValid)
{
// 保存操作
return new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
用AOP的方式去实现验证
或者我们可以换成下面的这种方式,先创建一个Filter。
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
再到Post和PUT的方法上面打上这个标签。
[HttpPut]
[ValidateModel]
public void Put(Event item)
{
if (!repository.Update(item))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
我们还需要在我们的WebApiConfig中注册这个Filter。
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ValidateModelAttribute());
}
}

前端拿到这个消息之后,就可以通知给用户了。当然最后还是需要加上前端验证,可以大大的提高用户体验以及减轻服务器的压力。
小结
没有小结,大伙都散了吧!我要骑车上班去了,你呢?
bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序的更多相关文章
- Knockout, Web API 和 ASP.Net Web Forms 进行简单数据绑定
使用Knockout, Web API 和 ASP.Net Web Forms 进行简单数据绑定 原文地址:http://www.dotnetjalps.com/2013/05/Simple-da ...
- ASP.NET MVC+Knockout+Web API+SignalR
架构设计(ASP.NET MVC+Knockout+Web API+SignalR) 架构设计(ASP.NET MVC+Knockout+Web API+SignalR) 2014-01-16 18: ...
- simple-spa 一个简单的单页应用实例
上两篇文章说过要写一个简单的单页应用例子的, 迟迟没有兑诺, 实在有愧 哈哈.这篇写给小白用户哈. 正好趁今天风和日丽,事情不多, 把从项目里的代码扣取了一下, 整理了一个简单的例子.因为我们生产项目 ...
- 【翻译】使用Knockout, Web API 和 ASP.Net Web Forms 进行简单数据绑定
原文地址:http://www.dotnetjalps.com/2013/05/Simple-data-binding-with-Knockout-Web-API-and-ASP-Net-Web-Fo ...
- knockoutjs+ jquery pagination+asp.net web Api 实现无刷新列表页
Knockoutjs 是一个微软前雇员开发的前端MVVM JS框架, 具体信息参考官网 http://knockoutjs.com/ Web API数据准备: 偷个懒数据结构和数据copy自官网实例 ...
- 使用 AngularJS 开发一个大规模的单页应用(SPA)
本文的目标是基于单页面应用程序开发出拥有数百页的内容,包括认证,授权,会话状态等功能,可以支持上千个用户的企业级应用. 下载源代码 介绍 (SPA)这样一个名字里面蕴含着什么呢? 如果你是经典的S ...
- 基于Vue2.0+Vue-router构建一个简单的单页应用
爱编程爱分享,原创文章,转载请注明出处,谢谢!http://www.cnblogs.com/fozero/p/6185492.html 一.介绍 vue.js 是 目前 最火的前端框架,vue.js ...
- 架构设计(ASP.NET MVC+Knockout+Web API+SignalR)
最近忙于重构项目的架构设计,没有时间发博客,也没有时间回复邮件及博文评论,忘各位见谅: 今天先发布架构设计图,同样没有时间写相关的介绍也没有时间回复评论,所以就不发在首页,希望给看到的朋友一些参考,同 ...
- 快速构建一个简单的单页vue应用
技术栈 vue-cli webpack vux,vux-loader less,less-loader vue-jsonp vue-scroller ES6 vue-cli:一个vue脚手架工具,利用 ...
随机推荐
- 避免重复造轮子的UI自动化测试框架开发
一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...
- const,static,extern 简介
const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...
- mybatis_基础篇
一.认识mybatis: MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改 ...
- Adaboost提升算法从原理到实践
1.基本思想: 综合某些专家的判断,往往要比一个专家单独的判断要好.在"强可学习"和"弱科学习"的概念上来说就是我们通过对多个弱可学习的算法进行"组合 ...
- zookeeper源码分析之二客户端启动
ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些常用的API: create(path, data, flags): 创建一个ZNode, path是其 ...
- 谈谈JS的观察者模式(自定义事件)
呼呼...前不久参加了一个笔试,里面有一到JS编程题,当时看着题目就蒙圈...后来研究了一下,原来就是所谓的观察者模式.就记下来...^_^ 题目 [附加题] 请实现下面的自定义事件 Event 对象 ...
- WebGIS中等值线前端生成绘制简析
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 等值线是GIS制图中常见的功能,一般有两种思路:一种是先进行插 ...
- 【用户交互】APP没有退出前台但改变系统属性如何实时更新UI?监听系统广播,让用户交互更舒心~
前日,一小伙伴问我一个问题,说它解决了半天都没解决这个问题,截图如下: 大概楼主理解如下: 如果在应用中有一个判断wifi的开关和一个当前音量大小的seekbar以及一个获取当前电量多少的按钮,想知道 ...
- CSS三个定位——常规、浮动、绝对定位
.dage { width: 868px; background: #5B8C75; border: 10px solid #A08C5A; margin-top: -125px; margin-le ...
- iOS之应用版本号的设置规则
版本号的格式:v<主版本号>.<副版本号>.<发布号> 版本号的初始值:v1.0.0 管理规则: 主版本号(Major version) 1. 产品的主体构件进 ...