在前面的文章里我谈到了先后端分离的一些见解,这个见解是从宏观的角度来思考的,没有具体的落地实现,今天我将延续上篇文章的主题,从纯前端的架构设计角度谈谈先后端分离的一种具体实现方案,该方案和我原来设想有了很大的变化,可是核心思想没变,就是控制层是属于Web前端的。javascript
在之前文章里我说道先后端分离的核心在于把mvc的控制层归为前端的一部分,原方案的构想在实际的生产开发里很难作到,我以为核心仍是控制层和视图层的技术异构性,这样后果使得系统改造牵涉面太大,致使在项目团队里,沟通、协调以及管理成本相对较高,随着前端技术的发展,前端开发的工程量是愈来愈大,难度也是愈来愈高,所以前端工程的项目化,工程化和独立性愈来愈被人重视了,因此出现了大量的javascript MVC的富应用。若是javascript也能作到MVC模式,那么前端框架就能够抛弃异构语言的控制层,作到真正的独立。css
要把传统的MVC的C层从前端剥离掉,咱们首先要理解下MVC的C层即控制层到底作了什么样的事情,控制层的做用是模型层和视图层沟通的纽带,模型层进一步具体点就是数据层,视图层具体点就是数据展现给用户的方式,下面咱们看看java的Web应用里,控制层和视图层是如何耦合的呢?作过java的web开发的人第一个反应就是页面里回嵌入大量java代码或者使用jsp的标签或者使用velocity,freemark这样的模板语言,这些东西你们很天然的把它们归为服务端的东西,可是它们却出如今了视图层,因此视图层和控制层无法解耦,其实除了这个还有一个你们很熟悉但又不多把它归为是视图层和控制层的耦合因素,这个因素就是页面的跳转,用ajax的角度描述这个因素就是页面的同步提交,这两个因素也就是构成了控制层的核心应用(是应用数据和页面展现的桥梁)。html
要将前端独立起来,控制层归为前端是不可改变的定律,若是服务端的人太傲慢无礼,不愿放掉控制层,那么前端就必须本身来作控制层,那么前端作控制层的难点就是解决前端技术如何作到解决服务端数据展现和有时没法避免的同步提交问题,解决服务端数据展现问题就是要在javascript语言里找到替换java代码、jsp标签以及velcity这样的模板语言的技术,很幸运在javascript的确拥有这样的技术,这就是微软公司贡献的jQuery的模板语言jquery-tmpl,它的访问地址是:前端
https://github.com/BorisMoore/jquery-tmpljava
百度搜索的地址:jquery
有了javascript的模板技术,咱们就能够不用在页面再写入服务端的任何东西,这样就达到异构语言的控制层和前端的分离,从而摒弃服务端的控制层。而模板语言须要的数据就能够经过ajax请求发送到页面,ajax接收到数据后传输到javascript的模板语言里最终就能够达到服务端数据在页面上的展现,使用这种方式展现数据好处不只仅局限于控制层和视图层的解耦,同时还会提高页面的响应效率,由于经过这种方式,数据和服务端的交换再也不须要视图展现要素即服务端直接发送个页面,或者是页面的片断,而后操做dom使得这些视图性的东西展现到页面上的行为,而如今服务端只须要传输须要传输的数据就行,这样一个http请求的数据传输量会大大减小,减小http请求的数据大小是提高网站加载效率的重要指标之一,同时若是传输只须要最必要的数据,那么服务端和前端的交互就能够作到统一的报文格式,使用统一的报文规范,这样会对项目管理,系统运维和维护带来质的飞越。github
让控制层完全归为web前端的第二步就是要让web应用的同步提交操做完全死翘翘,而传统的同步提交只要承担整个web应用的入口的功能便可。谈到这里我想若是对web前端开发有过经验的人看到上面这句话就很容易联想到如今很火的单页面开发,没错个人确在讲单页面开发,其实javascript MVC的最高境界就是单页面模式。web
虽然时下的web应用是ajax的天下,可是若是咱们想完全的抛弃同步提交请求的想法真的应用到实践中,开发人员会发现它经常会变成一个吃力不讨好的事情,为何说它是一件吃力不讨好的事情,我想主要体如今两个方面:ajax
第一方面:ajax请求每每是做为纯数据的传输,那么页面效果的显示就须要开发人员本身操做DOM,使用各类javascript开发技巧,这就大大增长页面开发难度和复杂度,对于一个要投入市场的web应用,其成本和风险是可想而知的。
另外一方面:同步提交页面会让用户享受一种很顶级的用户体验,这就是浏览器的前进和后退体验,若是让ajax作前进和后退,特别是用户和网站交互量很大的网站,这个操做可能会成为一件不可能完成的任务。
这里我首先讲如何解决前进和后退的问题,在浏览器的请求url地址有一个很重要的特性就是hash属性,例如咱们写页面时候经常会写到这样的语句:
1
|
<
a
href=”#” onclick=”ftn()” id=”btn”>btn</
a
>
|
当用户点击这个连接时候,会促发click事件,可能不少人没有留心到此时网页请求的url后面会添加一个#号,例如:www.cnblogs.com/#,若是咱们把这个连接改下,以下:
1
|
<
a
href=”#sharpxiajun” onclick=”ftn()” id=”btn”>btn</
a
>
|
再点击这个连接,咱们会发现连接变成了www.cnblogs.com/#sharpxiajun,前面的#sharpxiajun就是url的hash,url的hash是不会发送给服务端的,不过在浏览器里有专门的事件能够监听到它,这个事件就是hashchange事件,它是一个window的事件,浏览器的前进与后退支持url的hash改变,同时window能够监听到该事件,所以咱们能够经过改变url的hash再加上ajax请求就能够模拟页面的同步提交了,同时该请求是可使用浏览器的前进和后退操做。
使用url的hash属性模拟同步的url,那么咱们就能够将页面的url改为一个带hash的url地址,例如传统网站的注册页面地址应该是:www.cnblogs.com/register.html,如今能够改成www.cnblogs.com/#!/register,若是注册页面的上游页面是www.cnblogs.com/,那么咱们在注册页面点击回退按钮时候页面就会跳转到www.cnblogs.com/。
若是咱们web前端拥有的以上我所讲述的技术,那么一个web应用的控制层能够彻底平移到了web前端,而web前端能够作到真正的项目独立,到时web前端只要和服务端创建合适的报文规范,使用时下流行的json数据格式,就能够完成Web应用的开发,这样的web应用就作到先后端的真正分离。
使用我讲到的技术开发网站,浏览器的通讯都将是ajax,这个ajax按应用场景能够分为两种类型:一种类型是模拟同步提交的ajax,这个请求时获取视图,也就是页面,这个功能能够当作一个路由功能,这是控制层控制视图层的操做,另外一个ajax就是获取数据,ajax获取到数据后,经过javascript模板技术进行转化,最后控制层将转化的数据和视图层结合到一块儿,最终将完整的页面呈现给网站的用户。
Javascript作控制层实际上是经过url的hash完成的,核心是使用window的hashchange事件,这里就有一个web前端开发最头疼的问题,是否是全部浏览器都支持hashchange事件了?答案是:新浏览器都支持,最可恶的ie,在8以上包括8都支持,那么咱们想让全部浏览器均可以使用hashchange怎么办来了?jquery有个插件可让低版本的浏览器支持hashchange事件,有兴趣的人能够百度一下,不过若是是在移动设备上开发web应用时彻底不用担忧兼容问题。
前面我用到一个带hash的url:www.cnblogs.com/#!/register,#!/xxx是我推荐给大伙的书写形式,理由是:咱们作一个网站都须要给搜索引擎示好,可是搜索引擎的网络爬虫都是抓取静态页面,对于ajax请求的静态页面每每无能为力,所以咱们要让搜索引擎的网络爬虫能找到咱们的页面因此在#后面加上!/,不少高级的搜索引擎会抓取到咱们网页上的内容,这里主要是指google,百度的是否是有相似的能力,俺就不清楚,这个就得问问度娘了。
我以前在个人博客里给大伙分享了我本身写的一个javascript框架,当时我使用一个文件,一个库来完成个人框架,这样的框架其实只能算是把一个Web开发里能通用的东西作了一个抽取和汇总,换种说法就是之前的框架是一个工具类大集合,可是到了我今天讲的web前端的javascript框架,这样的工具类是不能知足咱们的需求,缘由是控制层被移到了web前端,这样web应用的数据模型及模型层和控制层对视图层的路由功能同时也迁移到了web前端,那么某一个页面对应的数据模型以及对应的视图页面,咱们应该让它和别的数据模型和对应页面有一个明显的界线,具体实现里就是一个视图及一个页面对应的javascript代码应该要模块化,因此咱们最好一个页面对应一个javascript文件,这样咱们就要对javascript代码进行模块化管理,要引入requieJS或者国产的seajs工具,具体的使用有兴趣的童鞋能够百度或者google一下。
若是咱们不会用requieJS和seajs怎么办了?大型网站的javascript代码和css代码在生产上都会尽可能的合并成少许的文件,所以我这里建议你们要用面向对象的方式组织本身的javascript代码,这里我推荐一个借鉴与jQuery架构相似的javascript框架模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
(
function
($,params){
function
MyObj = {
return
this
._init.apply(
this
,arguments);
}
myObj.fn = myobj.prototype;
myObj.fn =
function
(){
return
{
_init:
function
(){
// 初始化方法
this
.setting = arguments;
return
this
;
},
_bindEvt:
function
(){
// 事件绑定
代码…
return
this
;
},
_pageLoad:
function
(){
_bindEvt();
代码…
return
this
;
}
}
};
// 给类添加属性或方法,至关于静态变量
myObj.extend =
function
(obj){
var
extended = obj. extended;
for
(
var
i
in
obj){
myObj.fn[i] = obj[i];
}
if
(extended) extend(myObj);
};
myObj.load =
function
(param){
return
new
myObj(params)._pageLoad();
}
return
myObj;
})(jQuery,params,undefined)
|
该框架的load方法至关于java的main函数,这里咱们使用javascript对象的技术构建对象,那么该对象对外就很容易扩展,每一个原型prototype方法都会返回this指针这样就能够达到jQuery里方法连缀的写法。
其实这样的结构最后也能应用到requieJS和seajs里面,这样写的javascript代码的结构性和层次性更好,扩展性更强。
好了文章写完了,本篇文章一鼓作气,不免有遗漏错误地方,到时还请认真的童鞋及时指出。