摘要:該內的內容會根據路由的變化而變化。配置,用來定義路由規則。由此我們就需要另一個第三方路由模塊,叫做,當然它是基于開發的。造成這種現象的最根本原因路由沒有明確的父子層級關系。監聽路由路由狀態發生改變時可以通過監聽,通過注入實現狀態的管理。
何為路由
路由機制運可以實現多視圖的單頁Web應用(single page web application,SPA)。
單頁應用在使用期間不會重新加載頁面,所有的數據交互由ajax完成。
非單頁應用請求不同url,對SEO很不友善,而且刷新或發送url無法保留瀏覽進度。
而單頁應用前端擁有監管url的權利,通過監聽頁面地址變化來調用前端MVC模塊,渲染不同頁面。當然,前端這時也擁有了修改URL的能力,只需要管理好前端視圖與URL。在確定了需要保持的頁面后,前后端圍繞頁面各自負責視圖的渲染。
路由實現 url不變話化這一種的情況是url完全不變,即你的頁面怎么改變,怎么跳轉url都不會改變。
這種情況的原理 就是純ajax拿到頁面模板后替換原頁面中的元素,重現渲染頁面。然而ajax的一個致命缺點就是導致瀏覽器后退按鈕失效。用戶體驗很不好。
這種類型的優點就是刷新頁面,頁面也不會丟。關于hash值的介紹鏈接
通過監聽 hash(#)的變化來執行js代碼 從而實現 頁面的改變
核心代碼:
window.addEventListener("hashchange",function(){ //code })
就是通過這個原理 只要#改變了 就能觸發這個事件,這也是很多單頁面網站的url中都也 (#)的原因
h5 history api這種類型是通過html5的最新history api來實現的。
通過pushState()記錄操作歷史,監聽window.onpopstate事件來進行視圖切換,能正常的回退前進。
這里是關于 history api的連接
關于這三種方法的demo往后會給大家提供完善,大家可以提醒我。
AngularJS 路由AngularJS 路由就是通過通過hash和h5 history兩種方式實現的,允許我們通過不同的 URL 訪問不同的內容。
通常我們的URL形式為 http://runoob.com/first/page,但在單頁Web應用中 AngularJS 通過 # + 標記 實現,例如:
http://cds.com/#/firstPage http://cds.com/#/secondPage
當我們點擊以上的任意一個鏈接時,向服務端請的地址都是一樣的 (http://runoob.com/)。 因為 # 號之后的內容在向服務端請求時會被瀏覽器忽略掉。 所以我們就需要在客戶端實現 # 號后面內容的功能實現。
AngularJS 路由通過 # + 標記 幫助我們區分不同的邏輯頁面并將不同的頁面(View)綁定到對應的控制器(controller)上。
下面我們介紹一下Angular的路由實現。
ngRouterngRouter是angular的一個多帶帶模塊。
我們看看他是怎么實現的。
1、加載實現路由的 js 文件:angular-route.js。
2、包含了 ngRoute 模塊作為主應用模塊的依賴模塊。(為什么這么寫,下文介紹)
angular.module("routingDemoApp",["ngRoute"])
3、使用 ngView 指令。
該 div 內的 HTML 內容會根據路由的變化而變化。
4、配置 $routeProvider,AngularJS $routeProvider 用來定義路由規則。
angular.module("MyApp", ["ngRoute"]).config(["$routeProvider", function($routeProvider){ //$routeProvider 為我們提供了 when(path,object) & otherwise(object) 函數按順序定義所有路由,函數包含兩個參數:第一個參數是 URL 或者 URL 正則規則。第二個參數是路由配置對象。 $routeProvider.when(url, { template: string,//在 ng-view 中插入簡單的 HTML 內容 templateUrl: string,//在 ng-view 中插入 HTML 模板文件 controller: string, function 或 array,//在當前模板上執行的controller函數,生成新的scope。 controllerAs: string,//為controller指定別名。 redirectTo: string, function,//重定向的地址。 resolve: objectUI-Router//指定當前controller所依賴的其他模塊。 }); $routeProvider.otherwise({redirectTo:"/"})//除配置路由外的其他路徑指向 }]);
在使用ngRouter時往往不能進行視圖嵌套,而且功能有限,不能滿足開發需求。由此我們就需要另一個第三方路由模塊,叫做 ui.router ,當然它是基于ngRouter開發的。
ui.Router提供了一種很好的機制,可以實現深層次嵌套。
1、在引入ui.route路由源文件
2、加載依賴模塊
angular.module("myApp", ["ui.router"]); // myApp為自定義模塊,依賴第三方路由模塊ui.router
在程序啟動(bootstrap)的時候,加載依賴模塊(如:ui.router),將所有 掛載 在該模塊的 服務(provider) , 指令(directive) , 過濾器(filter) 等都進行注冊 ,那么在后面的程序中便可以調用了。
angular.module("myApp", ["ui.router","myFilter","myDirective","myService"]); //實際中只要依賴首頁需要用的模塊就可以。
3、定義視圖
//視圖展示 dash頁//定義導航
在視圖模板中還可以包含自己的 ui-view ,這就是我們可以支持嵌套路徑的原因。
4、配置 $stateProvider,在ui.router通過$stateProvider來定義路由規則
angular.module("MyApp").config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) { //$stateProvider為我們提供了state(pathName,object) & otherwise(object) 函數按順序定義所有路由,函數包含兩個參數:第一個參數是 URL名稱(視圖中導航名稱)。第二個參數是路由配置對象。 $urlRouterProvider.otherwise("/dash.html"); //定義默認跳轉和除配置之外的定向路由 $stateProvider .state("dash"(string), {//導航用的名字,如dash里的login url: "/dash.html"(string),//訪問路徑,可視的路徑 template: string,//在 ui-view 中插入簡單的 HTML 內容 templateUrl: "views/dash.html"(string),//在 ui-view 中插入 HTML 模板文件(靜態文件路徑) data: {pageTitle: "系統主頁"}(object可以多層嵌套),//data數據不會注入到控制器,可以從父狀態向子狀態傳遞數據 controller: "DashController",//在當前模板上執行的controller函數,生成新的scope。 params:object,//params 選項是參數名稱(下文有栗子)。當狀態激活的時候,應用會使用這些參數填充 $stateParams 服務。 resolve: { deps: ["$ocLazyLoad", function ($ocLazyLoad) {//"$ocLazyLoad"是controller的依賴模塊,返回執行方法。AngularJs 通過 ocLazyLoad 實現動態(懶)加載模塊和依賴。具體的有時間再擴展。留個鏈接?。?! return $ocLazyLoad.load({ name: "MetronicApp", insertBefore: "#ng_load_plugins_before", files: [ "app/controllers/DashController.js", ] }); }] }//在下文介紹 }) }]);
resolve 功能路由嵌套
使用 resolve 功能,我們可以準備一組用來注入到控制器中的依賴對象,在 uiRoute 中,resolve 可以在路由實際渲染之前解決掉 promise。
resolve 選項提供一個對象,對象中的 key 就是準備注入 controller 的依賴名稱,值則是創建對象的工廠。
如果是一個字符串,就試圖用這個串來匹配當前已經注冊的服務名稱,
如果是一個函數,執行這個函數,返回的值就是依賴。
如果函數返回一個 promise,在控制器被實例化之前,將會被 resolved,返回的值被注入到 controller中。
先說說為什么要視圖嵌套,頁面一個主區塊顯示主內容,主內容中的部分內容要求根據路由變化而變化,這時就需要另一個動態變化的區塊嵌套在主區塊中。
如果用ngRoute 實現視圖嵌套,你會發現是不可能的,因為在ng-view指令link的過程中,代碼會無限遞歸下去。造成這種現象的最根本原因:路由沒有明確的父子層級關系。
而uiRouter很好的解決了這一問題。
慣例先上代碼:
$stateProvider .state("parent", { abstract: true,//可以通過它解決依賴問題,或者特定數據處理,或者簡單地同樣的 url 來嵌套多個路由,例如,所有路由都在 /parent下面。 url: "/parent",//相對路徑=>…./index.html#/parent template: "I am parent "http://當然你也可以寫在templateUrl中 }) .state("parent.child", { url: "/child",//相對路徑=>…./index.html#/parent/child url:"^/child",//絕對路徑=>…./index.html#/child 注意寫法區別 template: "I am child" }); 點我顯示父view內容 點我顯示父view與子view內容
巧妙地,通過 parent 與 parent.child 來確定路由的 父子關系 ,從而解決無限遞歸問題。另外子路由的模板最終也將被插入到父路由模板的div[ui-view]中去,從而達到視圖嵌套的效果。
多視圖效果多視圖:頁面可以顯示多個動態變化的不同區塊。
原因在于,在ui.router中:可以給視圖命名(字符串),如:ui-view="status"??梢栽诼酚膳渲弥懈鶕晥D名字(如:status),配置不同的模板(其實還有controller等)。
放一段代碼
var app = angular.module("myApp", ["ui.router"]); app.config(["$stateProvider", function ($stateProvider) { $stateProvider .state("index", { url: "/index", views:{ "header":{template:"頭部內容"}, "nav":{template:"菜單內容"}, "body":{template:"展示內容"} } }) }]);
待完善(項目無涉及)(控制起來也很麻煩,處理不好都是坑,如果要用到再完善吧。)
視圖定位@的作用 是用來絕對定位view,即說明該ui-view屬于哪個模板。
同樣放一段代碼
show index content111111 content222222var app = angular.module("myApp", ["ui.router"]); app.config(["$stateProvider", function ($stateProvider) { $stateProvider .state("index", { url: "/index", views:{ "index":{template:""}, //這里必須要絕對定位 "header@index":{template:"頭部內容header"}, "nav@index":{template:"菜單內容nav"}, "body@index":{template:"展示內容contents"} } }) //絕對定位 .state("index.content1", { url: "/content1", views:{ "body@index":{template:"content11111111111111111"} //"body@index"表時名為body的view使用index模板 } }) //相對定位:該狀態的里的名為body的ui-view為相對路徑下的(即沒有說明具體是哪個模板下的) .state("index.content2", { url: "/content2", views:{ "body":{template:"content2222222222222222222"}// } }) }]);待完善(項目無涉及)(其實也不難理解,只是這個時間段任務重了,這里有時間再了解完善?。?。
路由傳參Angular應用通過通過$stateParams服務獲取參數他有兩種方式:(相對溫習一下路徑穿參方式,$location.search())
1.url: "/index/:id"
2.url: "/index/{id}"
注意這里只是兩種不同寫法angular.module("MetronicApp").config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) { $stateProvider .state("dash", { url: "/dash.html/:orderId/:projectId", //或者注意是或者(這種需要直接定義好,或由頁面傳值) url:"/dash.html/{orderId}", //如果應用訪問 /dash.html/42,那么,$stateParameter.orderId 就成為 42, 實際上, $stateParams 的值將為 { orderId: 42 } //或者(這種是在路由中定義好參數) url:"/dash.html", params: { orderId: { value: 42} } templateUrl: "/dash.html", controller: function ($stateParams,$scope) { //可以注入$stateParams服務來獲取你所傳遞的服務 $scope.projectId=$stateParams.projectId; $scope.orderId=$stateParams.orderId; //42 } }) }]); //然后大家在看頁面中的寫法 也是兩種方式建議用第二種() href傳參數 ui-sref傳參數再來擴展一下,在控制器中的頁面跳轉,并且傳參
angular.module("MetronicApp",["ui.router"]); angular.module("MetronicApp").controller("MyCtrl", function($scope, $state,$location) { $scope.pathChange = function() { $state.go("dash",{name: 42}); //第一個參數是定義的urlName,第二個為參數 }; $scope.pathChange = function() { $location.path("/sixth/detail/42"); //$location服務中有介紹 }; });可能大家會有疑問,我們項目的路徑和這個示例不一樣,這里的參數是angular是控制路徑跳轉的參數目的在與定向視圖或者視圖之間丶控制器之間的通信,不是從后臺請求的,這個要搞清楚。
和之前說的$rootScope通信效果是一樣的。
監聽路由Angular 路由狀態發生改變時可以通過"$stateChangeStart"、"$stateChangeSuccess"、"$stateChangeError"監聽,通過注入"$location"實現狀態的管理。
代碼示例如下:function run($ionicPlatform, $location, Service, $rootScope, $stateParams) { //路由監聽事件 $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) { console.log(event); //該事件的基本信息 console.log(toState); //我們可以得到當前路由的信息,比如路由名稱,url,視圖的控制器,模板路徑等等 console.log(toParams); //我們可以得到當前路由的參數 console.log(fromState); //我們可以得到上一個路由的信息,比如路由名稱,url,視圖的控制器,模板路徑等等 console.log(fromParams); //我們可以得到上一個路由的參數 if (toState.name == "dash") { //獲取參數之后可以調請求判斷需要渲染什么頁面,渲染不同的頁面通過 $location 實現 if (toParams.id == 10) { //$location.path();//獲取路由地址 // $location.path("/validation").replace(); // event.preventDefault()可以阻止模板解析 } } }) // stateChangeSuccess 當模板解析完成后觸發 $rootScope.$on("$stateChangeSuccess", function(event, toState, toParams, fromState, fromParams) { //code }) // $stateChangeError 當模板解析過程中發生錯誤時觸發 $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) { //code }) }在頁面渲染中 可通過"$viewContentLoading"和 "$viewContentLoaded"監聽頁面渲染狀態:渲染開始和渲染結束。(在控制器中添加以下代碼實現監聽)
// $viewContentLoading- 當視圖開始加載,DOM渲染完成之前觸發,該事件將在$scope鏈上廣播此事件。 $scope.$watch("$viewContentLoading",function(event, viewConfig){ alert("模板加載完成前"); }); //$viewContentLoaded- 當視圖加載完成,DOM渲染完成之后觸發,視圖所在的$scope發出該事件。 $scope.$watch("$viewContentLoaded",function(event){ alert("模板加載完成后"); });我們可以用這些監聽事件來判斷用戶傳參,權限判定等等。
工作原理我們了解一下uiRouter路由的工作原理
路由的創建
大致可以理解為:一個 查找匹配 的過程。就是將 hash值 (#xxx)與一系列的 "路由規則" 進行查找匹配,匹配出一個符合條件的規則,然后根據這個規則,進行數據的獲取,以及頁面的渲染。
我們分兩步學習我們通過調用 $stateProvider.state(...) 方法,創建了一個簡單路由規則(詳情看上文)
當我們反問http://...index.html#/dash.html的時候,這個路由規則被匹配到,對應的模板會被填到某個 [ui-view] 中。
它做了些什么呢。首先,創建并存儲一個state對象,里面包含著該路由規則的所有配置信息。
然后,調用 $urlRouterProvider.when(...) 方法(上文說過ui-router是基于ngRouter),進行路由的 注冊 (之前是路由的創建),代碼里是這樣寫的:$urlRouterProvider.when(state.url, ["$match", "$stateParams", function ($match, $stateParams) { // 判斷是否是同一個state || 當前匹配參數是否相同 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) { $state.transitionTo(state, $match, { inherit: true, location: false }); } }]);當 hash值 與 state.url 相匹配時,就執行后面那段回調,回調函數里面進行了兩個條件判斷之后,決定是否需要跳轉到該state。
至于說為什么說 "跳轉到該state,而不是該url"? 其實 ui.router是基于state(狀態)的,而不是url,之前就說過,路由存在著明確的 父子關系 ,每一個路由可以理解為一個state,當程序匹配到某一個子路由時,我們就認為這個子路由state被激活,同時,它對應的父路由state也將被激活。我們還可以手動的激活某一個state,就像上面寫的那樣, $state.transitionTo(state, ...) ,這樣的話,它的父state會被激活(如果還沒有激活的話),它的子state會被銷毀(如果已經激活的話)。
接著回到路由注冊,路由注冊調用了 $urlRouterProvider.when(...) 方法,它創建了一個rule,并存儲在rules集合里面,之后的,每次hash值變化,路由重新查找匹配都是通過遍歷這個 rules 集合進行的。
路由的查找匹配當路由的創建和注冊,接下來,就是路由的查找匹配了。這是一個復雜而又繁瑣的過程,繞到我都有點不想說。
angular 在剛開始的$digest(解析,臟查詢,能做的事情很多)時, $rootScope 會觸發 $locationChangeSuccess 事件(angular在每次瀏覽器hash change的時候也會觸發 $locationChangeSuccess事件)ui.router 監聽了 $locationChangeSuccess 事件,于是開始通過遍歷一系列rules,進行路由查找匹配當匹配到路由后,就通過 $state.transitionTo(state,...) ,跳轉激活對應的state最后,完成數據請求和模板的渲染
來上一段源碼function update(evt) { // ...省略 function check(rule) { var handled = rule($injector, $location); // handled可以是返回: // 1. 新的的url,用于重定向 // 2. false,不匹配 // 3. true,匹配 if (!handled) return false; if (isString(handled)) $location.replace().url(handled); return true; } var n = rules.length, i; // 渲染遍歷rules,匹配到路由,就停止循環 for (i = 0; i < n; i++) { if (check(rules[i])) return; } // 如果都匹配不到路由,使用otherwise路由(如果設置了的話) if (otherwise) check(otherwise); } function listen() { // 監聽$locationChangeSuccess,開始路由的查找匹配 listener = listener || $rootScope.$on("$locationChangeSuccess", update); return listener; } if (!interceptDeferred) listen();看懂的朋友就會發現一個問題,每次路由變化(hash變化),由于監聽‘$locationChangeSuccess"事件,都要進行rules的遍歷 來查找匹配路由,然后跳轉到對應的state。我們之所以要循環遍歷rules,是因為要查找匹配到對應的路由(state),然后跳轉過去,倘若不循環,也是能直接找到對應的state。在用ui.router在創建路由時:會實例化一個對應的state對象,并存儲起來(states集合里面)。每一個state對象都有一個state.name進行唯一標識(如:"dash")。這時候就體現出 ui-sref指令的大作用了。
通過ui-sref跳轉到dash.html當點擊這個a標簽時,會直接跳轉到dash.html,而并不需要循環遍歷rules。這個元素折行了一個方法,還是直接看代碼
element.bind("click", function(e) { // .. var transition = $timeout(function() { // 手動跳轉到指定的state $state.go(ref.state, params, options); }); });ui-sref="dash"指令會給對應的dom添加 click事件 ,然后根據dash(state.name),直接跳轉到對應的state。
跳轉到對應的state之后,ui.router會做一個善后處理,就是改變hash,此時就會觸發’$locationChangeSuccess"事件,然后執行回調,但是在回調中可以通過一個判斷代碼規避循環rules。
代碼段:function update(evt) { var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl; // 手動調用$state.go(...)時,直接return避免下面的循環 if (ignoreUpdate) return true; }所以我們在使用中可通過ui-serf來實現路由,達到視圖切換,或者在controller中調用 $state.go(....)來實現。
結語路由介紹的就這么多,但是任然要仔細逐行閱讀。也有一部分拓展,項目中沒有實際用到,但是也算是給優化做一些思路。與大家共勉。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92713.html
相關文章
AngularJS簡述
流行框架 簡介 angularjs是一款非常優秀的前端高級JS框架,由谷歌團隊開發維護,能夠快速構建單頁web應用,化繁為簡 無論是angularjs還是jQuery都是用原生JS封裝的 庫:對代碼進行封裝,調用封裝的方法,簡化操作 傳統方式是用get方式獲取元素,然后點方法 jQuery庫實現了對獲取方式的封裝,對方法的封裝 框架:提供代碼書寫規則,按照規則去寫代碼,框架會幫我們實現響應的功能...
Vue面試中,經常會被問到的面試題/Vue知識點整理
摘要:可以在該鉤子中進一步地更改狀態,不會觸發附加的重渲染過程。我工作中只用到,對和不怎么熟與的區別相同點都支持指令內置指令和自定義指令都支持過濾器內置過濾器和自定義過濾器都支持雙向數據綁定都不支持低端瀏覽器。 看看面試題,只是為了查漏補缺,看看自己那些方面還不懂。切記不要以為背了面試題,就萬事大吉了,最好是理解背后的原理,這樣面試的時候才能侃侃而談。不然,稍微有水平的面試官一看就能看出,是...
angularjs+springMvc學習筆記
摘要:回調說白了,就是把函數當參數傳給另一根函數,在另一個函數執行時調用此函數例如,在下面這段代碼中,上面定義了兩個函數和,下面的方法請求成功執行,失敗執行異步異步的原理我看了網上的一些博客和例子,大都以定時任務為例子說明,但具體的原理我還是不太 回調 說白了,就是把函數當參數傳給另一根函數,在另一個函數執行時調用此函數例如,在下面這段代碼中,上面定義了兩個函數success和error,下...
angularjs+springMvc學習筆記
摘要:回調說白了,就是把函數當參數傳給另一根函數,在另一個函數執行時調用此函數例如,在下面這段代碼中,上面定義了兩個函數和,下面的方法請求成功執行,失敗執行異步異步的原理我看了網上的一些博客和例子,大都以定時任務為例子說明,但具體的原理我還是不太 回調 說白了,就是把函數當參數傳給另一根函數,在另一個函數執行時調用此函數例如,在下面這段代碼中,上面定義了兩個函數success和error,下...
發表評論
0條評論
閱讀 1355·2019-08-30 15:44
閱讀 2099·2019-08-30 11:04
閱讀 518·2019-08-29 15:17
閱讀 2540·2019-08-26 12:12
閱讀 3133·2019-08-23 18:09
閱讀 922·2019-08-23 15:37
閱讀 1522·2019-08-23 14:43
閱讀 2920·2019-08-23 13:13