AngularJS路由二三事(一):ngRoute

轉自 http://blog.gejiawen.com/2015/12/15/routes-solution-on-angular-ng-route/?utm_source=tuicool&utm_medium=referral

前言

AngularJS中,關于路由的設計和用法是一個很重要的方面。簡單來說AngularJS的路由其實是一種純前端的解決方案。不同于后端路由,它的本質其實是:當請求一個url時,根據路由配置匹配這個url,然后請求模板片段,并插入到ng-view中去。所以從某種意義上來說,AngularJS的路由更加傾向通過改變url來進行頁面的局部刷新。

AngularJS路由二三事這個專題文章中,我將基于AngularJS 1.5版本,結合內置的ngRoute服務、ui-router模塊,以及ui-router-extras模塊來詳細闡述AngularJS中路由的相關內容。

文章中涉及到的示例代碼在我的github上,就是這個倉庫,可供參考。

另外提一點,因為AngularJS的官網在國內訪問可能不太穩定,所以可能對查閱文檔造成一些干擾。我們可以選擇查閱AngularJS中文站提供的文檔鏡像,但是這個文檔并不是緊跟AngularJS官方的版本號的。另一種途徑就是,我們可以將AngularJS的源代碼clone到本地,然后安裝好所有的依賴之后在本地build一下,然后grunt webserver就可以在本地起一個AngularJS的官方網站,此時就可以無障礙的查閱相關文檔了。

ngRoute

本篇文章我們將介紹如何使用AngularJS內置的ngRoute模塊來做前端路由。

我不太記得AngularJS是從哪個版本開始將ngRoute獨立成一個單獨的module,貌似是1.2之后吧,現在如果要使用ngRoute需要額外加載這個模塊文件,就像下面這樣,

除了angular-route模塊,還有angular-animateanglar-ariaangular-cookies等模塊在使用時也需要額外引入相關文件。這地方有點小坑,大家注意一下就可以了。

使用說明

ngRoute模塊中包含以下內容,

名稱所屬作用

ngViewDIRECTIVE提供不同路由模板插入的視圖層

$routeProviderPROVIDER提供路由配置

$routeSERVICE用于構建各個路由的url、view、controller這三者的關系

$routeParamsSERVICE解析返回路由中帶有的參數

上表中的每一個組件在路由中都扮演著不可或缺的作用。基本上使用AngularJS配置路由的基本流程是這樣的,

在主模板中使用ngView定義一個路由模板的視圖層。不同路由對應的模板將會插入到這個ngView所在的dom元素下。

使用$routeProvider進行路由配置,包括每一個路由對應的url,template以及controller。除了這些基本的配置之外,還會有一些額外的配置,比如resolve配置等。

在每個路由的controller中完成對應的業務邏輯。

可以通過注入$routeParams服務來獲取路由url上的參數;還可以通過$rootScope來監控$routeChangeStart和$routeChangeSuccess事件。

簡單實例

在實例代碼倉庫中有一個demo001文件夾,其目錄結構如下,

-index.html

-home.html

-post.html

-about.html

-index.js

其中index.html是我們的主頁面文件,其內容如下,

Angular Route Demo


home

post

about


注意,我們的頁面上有一個ng-view指令。

在index.js中,我們需要聲明一個AngularJS的module叫做demo001,并且做一些路由配置工作。代碼如下,

angular.module('demo001', ['ngRoute'])

.config([

'$routeProvider',

function($routeProvider){

$routeProvider

.when('/home', {

templateUrl:'home.html',

controller:'HomeController'

})

.when('/post', {

templateUrl:'post.html',

controller:'PostController'

})

.when('/about', {

templateUrl:'about.html',

controller:'AboutController'

})

.otherwise('/home')

}

])

這里有3點需要注意,

在聲明demo001這個module的時候,需要將ngRoute作為依賴。否則報$routeProvider未定義這樣的錯誤。

在module的configuration中,我們調用$routeProvider.when來配置不同路由的具體信息。$routeProvider.when方法接受2個參數,第一個是路由的url。第二個路由的具體配置,包括對應的模板地址,控制器名稱等。

$routeProvider.otherwise可以用于設置默認路由地址。簡單來說就是將未設置的url自動重定向到此url。

在我們補充完各個路由的控制器后,我們打開index.html就可以預覽了。在預覽時,注意點擊不同鏈接時url的變化,還可以觀察瀏覽器的Network行為。所有的子模板默認加載一次之后就會被緩存。

模塊化實例

在經過上面的實例之后,應該對AngularJS路由的基本用法有所了解了。現在我們來假定有這樣一個場景,假設我們的項目比較復雜,內部的模塊很多。此時更優的一種方案是,基于AngularJS來做模塊化設計與開發。AngularJS的模塊化是以它的module以及依賴注入等行為作為基礎的。

在實例代碼倉庫中有一個demo002的文件夾,其目錄結構如下,

-index.html

-index.js

-home.html

-home.js

-post.html

-post-id.html

-post.js

-about.html

-about.js

index.html與上一個實例相比基本沒有變化。然后我們再看一眼index.js,

angular.module('demo002', [

'ngRoute',

'Module.Home',

'Module.Post',

'Module.About'

])

.config([

'$routeProvider',

function($routeProvider){

$routeProvider.otherwise('/home');

}

])

與之前不同的是,我們在聲明demo002這個module時,附加了額外3個module。在路由的配置中,也僅僅只有一個$routeProvider.otherwise的設置。

這里我們就是使用了模塊化的思想,將/home,/post,/about這幾個路由抽象成獨立的module,將他們內部的所有邏輯和設置都封裝在內部。比如下面的home.js

angular.module('Module.Home', ['ngRoute'])

.config([

'$routeProvider',

function($routeProvider){

$routeProvider.when('/home', {

templateUrl:'home.html',

controller:'HomeController'

});

}

])

.controller('HomeController', [

'$scope',

function($scope){

$scope.msg ='This is home page';

}

]);

AngularJS的module機制和依賴注入機制,為模塊化設計提供了基礎。在稍微復雜一點的angularjs項目中我是非常推薦使用模塊化開發的,能夠抽象成獨立module的不僅僅是不同的路由模塊,可以是一個公共組件,也可以是一個公共服務等等。

路由參數

在上一個模塊化實例中,我們的post.js模塊如下,

angular.module('Module.Post', ['ngRoute'])

.config([

'$routeProvider',

function($routerProvider){

$routerProvider

.when('/post', {

templateUrl:'post.html',

controller:'PostController'

})

.when('/post/:postId', {

templateUrl:'post-id.html',

controller:'PostIdController'

})

}

])

.controller('PostController', [

'$scope',

function($scope){

$scope.posts = [

{

name:'post1',

id:'post-001'

}, {

name:'post2',

id:'post-002'

}

]

}

])

.controller('PostIdController', [

'$scope',

'$routeParams',

function($scope, $routeParams){

$scope.msg ='post id: '+ $routeParams.postId;

}

]);

注意這里,我們使用$routeProvider配置的第二個路由是這樣的/post/:postId。路由中的/:postId其實是一個參數,它將匹配類似/post/001這種url,其中001就是這個:postId的值。

我們在路由對應的控制器中,可以通過$routeParams參數來獲取這個url參數。如下,

.controller('PostIdController', [

'$scope',

'$routeParams',

function($scope, $routeParams){

$scope.msg ='post id: '+ $routeParams.postId;

}

]);

依次類推,我們可以為路由的url設置多個參數,比如/post/:year/:month/:day/:postName這樣的路由,它將匹配/post/2015/12/15/angular-router-demo這樣的路徑。控制器中注入的$routeParams服務將會得到類似下面的對象,

{

"year":2015,

"month":12,

"day":15,

"postName":"angular-router-demo"

}

路由中的resolve

在前面我們已經說明,可以使用$routeProvider.when方法進行路由配置。這個$routeProvider.when方法接受2個參數,其中第一個是路由的url,第二個是路由的具體配置項目。

關于$routeProvider.when的具體用法可以參考官方的文檔。

這里我僅僅針對其中的一個配置項resolve進行一些說明。

我們先來假設一個場景。

比如我最近上班太累了,想來一場旅行。在旅行之前,我需要拿到一張機票。而旅游網站出票是需要時間的。

將這個場景抽象成AngularJS應用就是這樣的:

有兩個頁面,一個是上班頁面(/home),一個是拿到機票開始旅行頁面(/trip)。

默認處于上班頁面。可以通過導航到開始旅行頁面。

在進入旅行頁面之前,我們必須要有一張機票。

所以,這個場景中,我們的問題可以總結成,當我從/home進入/trip路由之前,必須要拿到一個機票數據。

在實例代碼倉庫中有一個demo003文件夾,其目錄結構如下,

-index.html

-index.js

- index2.js// resolve方案

- home.html

- trip.html

在index.js中,

.config([

'$routeProvider',

function($routeProvider){

$routeProvider

.when('/home', {

templateUrl:'home.html',

controller:'HomeController'

})

.when('/trip', {

templateUrl:'trip.html',

controller:'TripController'

})

.otherwise('/home');

}

])

.controller('TripController', [

'$scope',

'$timeout',

function($scope, $timeout){

$timeout(function(){

$scope.ticket ='上海 -> 澳大利亞'

},4000);

}

])

這里我們使用定時器$timeout來模擬一個耗時的出票操作。此時我們從/home->/trip時,頁面會白屏4秒鐘。意味著在進行url跳轉完畢的時候,我們就已經將/trip的模板插入到了ng-view中,但是此時/trip需要的數據還沒有準備好。

這種場景下,我們一般會有兩種方式去解決這個問題,

第一種方式:在/trip的模板和控制器中做一些視覺等待邏輯。比如在TripController中進行耗時操作時,我們可以臨時展示一個loading視覺,待耗時操作完畢之后,我們再將這個視覺隱藏即可。

第二種方式:在配置路由時,配置resolve選項。配置resolve選項意味著,在進入這個路由之前就必須等待resolve中的數據返回。

這里我們主要來看看第二種方式。在實例代碼倉庫的demo003文件夾下的index2.js中,我們是怎么做的,

.config([

'$routeProvider',

function($routeProvider){

$routeProvider

.when('/home', {

templateUrl:'home.html',

controller:'HomeController'

})

.when('/trip', {

templateUrl:'trip.html',

controller:'TripController',

resolve: {

ticket: ['$q','$timeout',function($q, $timeout){

vardeferred = $q.defer();

$timeout(function(){

deferred.resolve('上海 -> 澳大利亞');

},4000);

returndeferred.promise;

}]

}

})

.otherwise('/home');

}

])

.controller('TripController', [

'$scope',

'ticket',

function($scope, ticket){

$scope.ticket = ticket;

}

])

注意/trip路由的配置中,我們設置了一個resolve配置項。這個配置項包含了一個叫做ticket的key,它將返回一個promise(這里采用AngularJS內置的$q來實現promise)。其內部也是使用定時器做了一個耗時操作的模擬。

當我們的路由從/home->/trip時,會觸發resolve下的所有promise,只有當所有的promise都都被正確的resolve之后才會進行路由切換,才會將/trip的模板插入到ng-view中。其實此時$route會拋出一個$routeChangeSuccess的事件,這個事件會被$rootScope捕獲到。

若resolve中只要有一個promise沒有被正確的resolve,那么此時$route將會拋出一個$routeChangeError的事件,并且終止路由切換,雖然url中的地址可能的確發生了變化,但是/trip的模板并沒有插入到ng-view,且TripController也沒有被執行。

當所有的resolve配置都返回之后,AngularJS會將resolve中key作為對應控制器的一個依賴注入進去,然后我們在相應的controller中就可以使用了。比如,

.controller('TripController', [

'$scope',

'ticket',

function($scope, ticket){

$scope.ticket = ticket;

}

])

這里可以看出,上面提到的兩種預載入數據的方案其實是有著本質區別的。前者其實是在跳轉的目標路由上做一些額外的工作去適配耗時操作的視覺,此時目標路由的模板已經被載入ng-view,且相應的控制器也被執行了。而后者在跳轉目標路由之前做一些額外工作去預加載數據,當數據準備妥當才會去載入目標路由的模板和執行相應的controller。

關于在路由中使用resolve的示例,有興趣可參閱這篇文章以及這篇文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容

  • 通過AngularJS仿豆瓣一刻的案例:https://github.com/zhongxiaolian/doub...
    中小戀閱讀 1,771評論 1 21
  • 1、angularjs的幾大特性是什么? 雙向數據綁定、依賴注入、模板、指令、MVC/MVVM 2、列舉幾種常見的...
    秀才JaneBook閱讀 1,555評論 0 22
  • AngularJS是什么?AngularJs(后面就簡稱ng了)是一個用于設計動態web應用的結構框架。首先,它是...
    200813閱讀 1,631評論 0 3
  • 1、angularjs的幾大特性是什么? 雙向數據綁定、依賴注入、模板、指令、MVC/MVVM 2、列舉幾種常見的...
    2e9a10d418ab閱讀 1,381評論 0 10
  • AngularJSAngularJS 是一個 MV* 框架, 最適于開發客戶端的單頁面應用。它不是個功能庫,...
    一直以來都很好閱讀 909評論 0 0