在前面的文章裏我談到了先後端分離的一些見解,這個見解是從宏觀的角度來思考的,沒有具體的落地實現,今天我將延續上篇文章的主題,從純前端的架構設計角度談談先後端分離的一種具體實現方案,該方案和我原來設想有了很大的變化,可是核心思想沒變,就是控制層是屬於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代碼的結構性和層次性更好,擴展性更強。
好了文章寫完了,本篇文章一鼓作氣,不免有遺漏錯誤地方,到時還請認真的童鞋及時指出。