ngRouter和ui-router區別
在單頁面應用中要把各個分散的檢視給組織起來是通過路由機制來實現的。本文主要對 AngularJS 原生的 ngRoute
路由模組和第三方路由模組 ui.router
的用法進行簡單介紹,並做一個對比。
ngRoute
使用方法
1) 引入 angular-route
無論是 ngRoute
還是 ui.router
,作為框架額外的附加功能,都必須以 模組依賴
的形式被引入。
1 |
<script src="lib/angular-route.js"></script> |
2) 配置路由
1 2 3 4 5 6 7 8 9 |
var app = angular.module('ngRouteApp', ['ngRoute']); app.config(function($routeProvider){ $routeProvider .when('/Main', { templateUrl: "main.html", controller: 'MainCtrl' }) .otherwise({ redirectTo: '/tabs' }); |
服務與指令
ngRoute
路由模組名
$routeProvider
服務提供者,用來定義一個路由表,即位址列與檢視模板的對映,對應於 ui.router
中的 urlRouterProvider
和 stateProvider
$route
服務,完成路由匹配,並且提供路由相關的屬性訪問及事件,如訪問當前路由對應的 Controller,對應於下面的 $urlRouter
和 $state
$routeParams
服務,儲存了位址列中的引數,對應於下面的 $stateParams
ng-view
指令,用來在主檢視中指定載入子檢視的區域,對應於下面的 ui-view
ui.router
使用方法
1) 引入 angular-ui-router
1 |
<script src="lib/angular-ui-router.min.js"></script> |
2) 配置路由
1 2 3 4 5 6 7 8 9 10 |
var app = angular.module("uiRouteApp", ["ui.router"]); app.config(function($urlRouterProvider, $stateProvider) { $urlRouterProvider.otherwise("/index"); $stateProvider .state("Main", { url: "/main", templateUrl: "main.html", controller: 'MainCtrl' }) |
服務與指令
ui.router
路由模組名
$urlRouterProvider
服務提供者,用來配置路由重定向$stateProvider
服務提供者,用來配置路由
$urlRouter
服務$state
服務,用來顯示當前路由狀態資訊,以及一些路由方法(如:跳轉)$stateParams
服務,用來儲存路由匹配時的引數
ui-view
指令,路由模板渲染,對應的 dom 相關聯ui-sref
指令,連結到特定狀態
路由的建立
基本配置
呼叫 $stateProvider.state(...)
方法,並可配置以下引數
1 2 3 4 5 6 |
$stateProvider .state("Main", { url: "/main", templateUrl: 'main.html', controller: 'MainCtrl', }) |
parent
有兩種方式可以指定父子狀態關係。
一種是,使用點標記法,像本文最後巢狀檢視部分舉得栗子那樣:
1 |
.state("tabs.tab1", {}) |
另一種是,使用 parent
屬性
1 2 3 |
.state("tab1", { parent: 'tabs' // 也可是一個狀態物件, parent: tabs }) |
abstract
使用 abstract
可以為所有的子狀態提供一個基 URL
,這樣做的好處就是可以在抽象出來的這個狀態所對應的 html
頁面中來定義靜態資源。抽象模板不能被啟用。
1 2 3 4 5 6 7 8 9 10 |
$stateProvider .state('contacts', { abstract: true, url: '/contacts', templateUrl: 'contacts.html', }) .state('contacts.list', { url: '/list', templateUrl: 'contacts.list.html' }) |
resolve
resolve
在 state
配置引數中,是一個物件(key-value
),每一個 value
都是一個可以依賴注入的函式,並且返回的是一個 promise
(當然也可以是值)。
1 2 3 4 5 6 |
resolve: { 'myResolve': ['contacts', function(contacts){ return contacts.all(); }] } |
這樣做的目的:
- 簡化了
controller
的操作,將資料的獲取放在resolve
中進行,這在多個檢視多個controller
需要相同資料時,有一定的作用。 - 只有當
reslove
中的promise
全部resolved
(即資料獲取成功)後,才會觸發$stateChangeSuccess
切換路由,進而例項化controller
,然後更新模板。
路由控制
url
動態部分被稱為引數,有以下幾種方式設定
1) 使用花括號的方式可以設定一個正則表示式規則的引數:
1 2 |
//只會匹配 pageId 為1到8位的數字 url: "/pages/{pageId:[0-9]{1,8}}" |
可以通過 ?
來指定引數作為查詢引數
1 2 |
//比如匹配 href="/page?type='new'" url: "/page?type" |
如果需要不止一個查詢引數,用 &
分隔:
1 2 |
//比如匹配 ui-sref="page({type:'all', title:'test ui-router'})" url: "/page?type&title" |
路由的查詢匹配
angular
在剛開始的$digest
時,$rootScope
會觸發$locationChangeSuccess
事件(angular
在每次瀏覽器hash
change 的時候也會觸發$locationChangeSuccess
事件)ui.router
監聽了$locationChangeSuccess
事件,於是開始通過遍歷一系列rules
,進行路由查詢匹配列表項- 當匹配到路由後,就通過
$state.transitionTo(state,...)
,跳轉啟用對應的state
- 最後,完成資料請求和模板的渲染
在檢視中,建議使用 ui-sref="xxxState"
而不是 href="#/abc"
,這樣做能減少一遍 rules
迴圈的遍歷,提升效能。
兩者區別
ngRoute模組
是 Angular 自帶的路由模組,而 ui.router模組
是基於 ngRoute模組
開發的第三方模組。
ui.router
是基於 state
(狀態)的, ngRoute
是基於 url
的,ui.router模組
具有更強大的功能,主要體現在檢視的巢狀方面。
巢狀檢視
頁面某個動態變化區塊中,巢狀著另一個可以動態變化的區塊。
前面的栗子就是一個很好的業務場景。
在首頁中包含一個動態區塊:
1 2 3 4 |
<body ng-app="ngRouteApp"> <h3>AngularJS UI-Router Tabs</h3> <div ng-view></div> </body> |
在標籤頁中又包含動態區塊:
1 2 3 4 5 6 |
<div> <span><a href="#/tab1">Page-1</a></span> <span><a href="#/tab2">Page-2</a></span> <span><a href="#/tab3">Page-3</a></span> </div> <div ng-view></div> |
一執行,報了一個這樣的錯誤:
RangeError: Maximum call stack size exceeded
發現瀏覽器崩潰了,因為 ng-view
會陷入死迴圈,無限遞迴下去。
使用 ui.router
能很容易解決這個問題,因為它定義的路由有明確的父子關係,並通過 ui-view
指令將子路由模版插入到父路由模板的 <div ui-view></div>
中去,從而實現檢視巢狀。看程式碼:
1 2 3 4 5 6 7 8 9 |
$stateProvider .state("tabs", { url: "/tabs", templateUrl: "pageTab.html" }) .state("tabs.tab1", { url: "/tab1", templateUrl: "tab1.html" }) |
其他區別
ui-router(左) : ngRoute(右)
- 應用程式內的一個區域 : 應用程式中的 url
- 可以巢狀的層次結構 : 只是平面層次結構
- 名稱可以自定義 : 名稱只能是 url
- 通過名稱或 url 導航 : 只能通過 url 導航
- 可以存在多個檢視(ui-view) : 只能單一檢視(ng-view)
- 可以填充任何檢視 : 只能填充一個檢視
- 通過狀態填充某一部件 : 通過指令將填充某一部件