眾所周知,現(xiàn)在移動(dòng)Webapp越來(lái)越多,例如天貓、京東、國(guó)美這些都是很好的例子。而在Webapp中,又要數(shù)單頁(yè)面架構(gòu)體驗(yàn)最好,更像原生app。簡(jiǎn)單來(lái)說(shuō),單頁(yè)面App不需要頻繁切換網(wǎng)頁(yè),可以局部刷新,整個(gè)加載流暢度會(huì)好很多。
廢話就不多說(shuō)了,直接到正題吧,淺談一下我自己理解的幾種單頁(yè)面架構(gòu):
1、requirejs+angular+angular-route(+zepto)
最后這個(gè)zepto可有可無(wú),主要是給團(tuán)隊(duì)中實(shí)在用不爽angular的同學(xué),可以靈活修改一下頁(yè)面某些內(nèi)容。當(dāng)然,嚴(yán)謹(jǐn)?shù)捻?xiàng)目不應(yīng)該出現(xiàn)zepto。
2、requirejs+backbone+zepto+template
這個(gè)方案更靈活,MVC味道更濃,使用自定義的template模版庫(kù)
3、requirejs+route+template
這個(gè)方案最靈活,看破紅塵,針對(duì)簡(jiǎn)單的業(yè)務(wù)用最簡(jiǎn)單的方式,只需要路由和模版,不用MVC框架
4、react
個(gè)人感覺(jué),react更偏向于view層的組件,更native,但實(shí)施難度略高
說(shuō)到項(xiàng)目架構(gòu),往往要考慮很多方面:
方便:例如使用jquery,必然比沒(méi)有使用jquery方便很多,所以大部分網(wǎng)站都接入類似的庫(kù);
性能優(yōu)化:包括加載速度、渲染效率;
代碼管理:大型項(xiàng)目需要考慮代碼的模塊化,模塊間低耦合高內(nèi)聚,目的就為了團(tuán)隊(duì)合作效率;
可擴(kuò)展性:這個(gè)不用說(shuō)了。
學(xué)習(xí)成本:一個(gè)框架再好,團(tuán)隊(duì)新成員難以掌握,學(xué)習(xí)難度大,結(jié)果很容易造成代碼混亂。
而根據(jù)實(shí)際經(jīng)驗(yàn)來(lái)看,方便是必然首要地位,除此之外,應(yīng)該是代碼管理了。團(tuán)隊(duì)合作過(guò)程中,各種協(xié)作,代碼沖突等等,都會(huì)給一個(gè)優(yōu)秀框架帶來(lái)各種奇怪難題。所以,有好的框架還不夠,我們還需要根據(jù)自身業(yè)務(wù)和團(tuán)隊(duì)的情況,按需裁剪或者修改框架,找到最佳的實(shí)施方案。
接下來(lái),將分三個(gè)隨筆分別介紹一下我心目中前三種架構(gòu)的較好實(shí)施方案,而最后一種,跟前三種有種道不同不相為謀的感覺(jué),加上自己道行不夠,還是暫且不提了。
這一篇,先說(shuō)說(shuō)第一種:requirejs+angular+angular-route
移動(dòng)端單頁(yè)面Web相對(duì)多頁(yè)面來(lái)說(shuō),模塊化管理顯得非常重要,因?yàn)槿绻麤](méi)有模塊化,頁(yè)面初始化時(shí)就把所有的js和所有模版都加載進(jìn)來(lái),會(huì)導(dǎo)致首屏速度極慢。這一點(diǎn),大家都理解的。
所以,requirejs或者類似的模塊化框架是必不可少的。requirejs比較流行,配合grunt可以做好整套的自動(dòng)化工具,我們就以這個(gè)為例子吧。
首先,來(lái)看看demo項(xiàng)目的整體架構(gòu)。
除了類庫(kù)外,業(yè)務(wù)代碼都以模塊劃分目錄,這樣做便于實(shí)際開(kāi)發(fā)中,按模塊化合并js和html,也利于多人并行開(kāi)發(fā),各自修改不同的模塊,互不影響。
另外,說(shuō)說(shuō)三個(gè)重點(diǎn)的根目錄文件:
index.html,這個(gè)就是單頁(yè)面唯一一個(gè)html了,其他都只是片段模版(tpl.html)。一般可以把這個(gè)html放到動(dòng)態(tài)服務(wù)器上,保持零緩存,同時(shí)這里可以攜帶各種js版本控制信息和必要的用戶數(shù)據(jù)。
main.js,這個(gè)是由requirejs引入的第一個(gè)業(yè)務(wù)js,主要是配置requirejs;
router.js,這個(gè)是整個(gè)網(wǎng)站/app的路由配置,在實(shí)際部署中,可以把main.js和router.js合并。
相對(duì)angular的寫(xiě)法,這里由于使用requirejs管理全部模塊,所以index.html中不需要引入angular等,只是設(shè)置了一個(gè)帶ng-view屬性的div,用于充當(dāng)整個(gè)App的視圖區(qū)域。
data-baseurl是額外加入的屬性,主要好處是可以輕松在html(0緩存)中對(duì)js的url進(jìn)行修改。
data-main就是requirejs的標(biāo)準(zhǔn)寫(xiě)法了,跳過(guò)不說(shuō)。
requirejs的語(yǔ)法,說(shuō)來(lái)話長(zhǎng),簡(jiǎn)單在代碼中做了注釋。有興趣了解詳情的可以參考官網(wǎng):http://requirejs.org/;
angular可以參考:https://docs.angularjs.org/guide/filter
這里配置好requirejs后,就做第一步工作,引入angular和angular的路由配置,然后用angular.bootstrap(document, [‘webapp’]); 手工啟動(dòng)angular,這里webapp是router.js中定義的angular module。
上述代碼看起來(lái)長(zhǎng),實(shí)際很短,因?yàn)橛幸欢丫G色的注釋,嘿嘿。。。
如果大家用過(guò)angular-route,這里的語(yǔ)法就很簡(jiǎn)單,如果沒(méi)用過(guò),則建議直接閱讀angular-route源代碼中的注釋,非常清晰。
簡(jiǎn)單而言,就是when函數(shù)配置一個(gè)路由規(guī)則,對(duì)應(yīng)一個(gè)template和一個(gè)controller。otherwise就是默認(rèn)路由,也就是遇到一個(gè)未定義路徑的時(shí)候如何跳轉(zhuǎn)。
如果沒(méi)有使用requirejs,那么我們需要在路由配置前加載完全部controller。angular-route需要做的只是切換HTML模版,重新編譯,綁定新的controller。
但是這里用了requirejs,事情就變化了。我們要按需加載,不可能頁(yè)面剛加載就全部controller都load回來(lái),這樣得耗費(fèi)多少流量。。。
所以,這里利用了angular-route提供的resolve功能,也就是路由更改html前先把resolve里邊該做的事完成。
resolve的寫(xiě)法比較特殊,接受的是一個(gè)key:value對(duì)象,keyName將會(huì)導(dǎo)入到controller中(如果controller有注明依賴)。而value應(yīng)該是一個(gè)函數(shù),函數(shù)的寫(xiě)法類似controller,angular會(huì)自動(dòng)根據(jù)參數(shù)名導(dǎo)入相應(yīng)依賴的服務(wù),例如$q、$route。
上述例子中,module1.js定義了模塊1的controller,后續(xù)我們?cè)倏创a。
由于路由配置前還不存在這個(gè)controller,所以現(xiàn)在需要?jiǎng)討B(tài)注冊(cè)這個(gè)controller。也就是:
$controllerProvider.register('module1Controller', controller);
angular有太多牛逼的功能,但實(shí)際上我業(yè)務(wù)太簡(jiǎn)單,用不到。所以這里只演示了3種最簡(jiǎn)單的情況。
這里不得不說(shuō),由于雙向綁定,拉cgi和修改dom這些操作就變得非常簡(jiǎn)單了。
貌似一切解決了?這樣的模塊化似乎已經(jīng)很好,跳轉(zhuǎn)到某個(gè)模塊的時(shí)候才加載對(duì)應(yīng)的html和controller js。
但是對(duì)于追求極致的團(tuán)隊(duì)來(lái)說(shuō),模塊的html和js應(yīng)該打包在一起,一次請(qǐng)求就拉回來(lái),這樣能大大減少HTTP請(qǐng)求的時(shí)間。而現(xiàn)在按照angular-route,只能利用templateUrl單獨(dú)拉取一個(gè)html文件。
那么接下來(lái),我們?cè)賱?dòng)動(dòng)歪腦筋,修改一下。
首先,先修改一下angular-route的源代碼,這個(gè)源代碼非常精簡(jiǎn),不用太糾結(jié),狠狠的去修改就好了。
另外,想問(wèn)我為什么知道或者想到在這修改?咳咳咳,我會(huì)大搖大擺的說(shuō)我認(rèn)識(shí)angular-route的作者么?。。。。。。。開(kāi)玩笑,作者叫什么,我都沒(méi)去找,還說(shuō)認(rèn)識(shí)作者。其實(shí)就是逐步調(diào),稍加變量搜索,發(fā)現(xiàn)一些不對(duì)勁,就做了這個(gè)小刀。
再另外,有專家要拍板了,這樣亂修改,肯定帶來(lái)毛病。是的,我不得不說(shuō),我自己都沒(méi)徹底的檢查是否有問(wèn)題,但按照實(shí)際情況來(lái)看,暫時(shí)沒(méi)遇到問(wèn)題。
然后,做一個(gè)新的when配置:
這里用module2做例子,跟module1不同,這里初始設(shè)置的template是空字符串,然后在resolve中require回來(lái)后,動(dòng)態(tài)修改$route.current.template。
因?yàn)槲抑?,這個(gè)修改能趕在angular-route修改HTML前,也就是小把戲能湊效。
相應(yīng),看看module2怎么寫(xiě):
大功告成,這樣html模版就不由angular-route去接管了,而是由requirejs加載,我們可以控制的范圍和靈活性就變大了。
不過(guò),這里controller的函數(shù)寫(xiě)法可能會(huì)因?yàn)閴嚎s混淆時(shí)丟失了原來(lái)的參數(shù)名,所以,我們也可以采用顯式注入的方式:
到這里,整個(gè)架構(gòu)基本就成型了,webapp中每個(gè)模塊都能非常獨(dú)立,這樣對(duì)網(wǎng)站打開(kāi)速度和協(xié)同開(kāi)發(fā)都非常有好處。
但是,路由表的配置還是略復(fù)雜,每次大家都要寫(xiě)一大堆代碼,這不是我們想要的,那么可以抽取公用代碼,再優(yōu)化一下。
routeMap可以由服務(wù)器直出,實(shí)現(xiàn)0緩存,徹底解耦,更便于團(tuán)隊(duì)合作。
最后最后,由于requirejs和angular都有模塊管理,但兩個(gè)概念又不一致,這里說(shuō)說(shuō)我的看法:
requirejs模塊管理,不單單是代碼模塊化,還提供了模塊加載的功能;
angular模塊管理,更在乎的是代碼邏輯上的模塊化,避免全局變量污染,并不提供js文件層面的加載功能;
作為邏輯模塊管理,其實(shí)用requirejs的模塊管理就夠了,所以我覺(jué)得除了angular原生的controller、service外,我們業(yè)務(wù)相關(guān)的公用庫(kù),用requirejs吧。
聯(lián)系客服