Backbine.js實戰第七章----導航控制器

我叫阿狸貓發表於2014-11-15

    在前面的章節中曾提到,Backbone框架的最佳應用場景是構建一個邏輯複雜的單頁應用,即單頁富應用。隨著人名對這種應用的喜愛,希望提供或蒐藏應用在某一階段的URL或錨點地址,方便日後直接進入這個階段的功能頁。

    為了滿足這種需求,實現在單頁富應用中通過錨點或特殊格式的URL完成可分享、可收藏的功能,Backbone框架中提供了兩個重要的模型——導航控制器(router)和歷史(history),router封裝了相容各類瀏覽器history的方案,通過使用瀏覽器的hash物件和HTML5中的pushState方法,將某階段特殊的URL或錨點地址與既定的事件(event)或函式(action)相繫結。輸入這些URL地址時,對應完成不同的功能,從而實現在單頁富應用中分享和收藏的功能。


7.1 瀏覽器導航基礎

    在正式介紹Backbone中的導航控制器(router)之前,有必要了解下瀏覽器的導航功能基礎知識,包含瀏覽器視窗(window)的history、HTML5中history API和location物件。瞭解這些基礎知識,有利於對Backbone中的導航控制器工作原理的理解和掌握。

7.1.1 history物件

    在編寫JavaScript程式碼中,經常用到history物件,它的功能是儲存瀏覽器的歷史瀏覽記錄。出於對使用者隱私和安全性的考慮,history物件可以使用的方法相對較少,其中有兩個比較常用的方法——back和forward。

    (1)back方法的功能是返回瀏覽器歷史記錄中當前頁的上一頁,與瀏覽器的“後退”按鈕功能相同,呼叫格式如下。

     window.history.back();

    (2)forward方法的功能是進入瀏覽器歷史記錄中當前頁的下一頁,與瀏覽器的“前進”按鈕功能相同,呼叫格式如下。

     window.history.forward();

     接下來通過一個簡單示例進行介紹。

              示例 7-1 history物件的方法

1.功能描述    

    在專案中,新建兩個用於互訪的頁面7-1-a.html和7-1-b.html。在第一個頁面中新增一個按鈕和一個超級連結元素,如果在history物件中存在已前進的歷史記錄,則隱藏超級連結元素,否則隱藏按鈕。單擊超連結元素時,進入7-1-b.html頁面,單擊按鈕時,進入歷史記錄中當前頁的下一頁。在第二個頁面中新增一個按鈕,單擊該按鈕時,返回歷史記錄中當前頁的上一頁。

檔案7-1-a.html內容

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
<style type="text/css">
	body{
		font-size:12px;
	}
</style>
</head>
<body>
	<input type="button" id="btnforward" value="前進" onclick="window.history.forward();">
	<a href="7-1-b.html" id="lnkforward">前進</a>
</body>
<script type="text/javascript">
	var $obj_wh = window.history;
	if($obj_wh.length>2){
		$("#lnkforward").css("display","none");
	}else{
		$("#btnforward").css("display","none");
	}
</script>
</html>
檔案7-1-b.html內容

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
<style type="text/css">
	body{
		font-size:12px;
	}
</style>
</head>
<body>
	<input type="button" value="後退" onclick="window.history.back();"/>
</body>
</html>


2.原始碼分析

    在本示例第一個頁面的JavaScript程式碼中,首先定義一個名為“$obj_wh”的變數用於儲存history物件。該物件的length值大於2時,表示在該物件中已存在當前頁的下一頁連線。通過history物件length值跟蹤發現。首次開啟瀏覽器時,該屬性值為1,而瀏覽第一個頁面地址時,該屬性值又變為2。可見,每瀏覽一個新的地址時,都會向history物件中新增一條記錄,而對應的length屬性值有會相應地增加1。所以該屬性值大於2時,表名瀏覽器已開啟過本示例的第二個頁面,因此就會隱藏連結元素,顯示“前進”按鈕。

    在第一個頁面新增“前進”按鈕時繫結onclick事件,單擊該按鈕時,將呼叫history物件中的forward方法直接進入當前頁歷史記錄中的下一頁。

    在第二個頁面新增“後退”按鈕時繫結onclick事件,單擊該按鈕時,將呼叫history物件中的back方法直接進入當前頁歷史記錄的上一頁。

    在history物件中,呼叫該物件forward和back方法分別實現頁面的前進和後退功能外,直接呼叫物件的go方法,也能實現頁面的前進和後退功能,呼叫格式如下。

    winodw.history.go(n);

    其中,引數n為一個整數,大於0時表示前進,小於0時表示後退,因此,如果是用於實現頁面的前進功能,下面程式碼是等價的。

    window.history.forward();

    等價於:

    window.history.go(1);

    如果是用於實現頁面的後退功能,則下列程式碼也是等價的。

    window.history.back();

    等價於:

    window.history.go(-1);



7.1.2 HTML5中history物件API

    上一節針對HTML4標準介紹了history物件的常用方法。HTML5基於原有物件方法新增了兩個實用的API方法。

   (1)pushState方法:功能是向歷史記錄堆疊的頂部新增一條記錄,常用於實現頁面的無重新整理跳轉,其呼叫格式如下。

     window.history.pushState(data,title[,url]);

    其中,data參數列示在新增記錄時傳遞的資料物件,該物件通常為JSON格式的字串;引數title為頁面顯示的標題,可選項引數為頁面跳轉地址,預設值為當前頁地址。

   (2)replaceState方法:功能是修改當前的歷史記錄值,其呼叫格式如下。

    window.history.replaceState(data,title[,url]);

    其中,各個從引數的使用說明與pushState方法相同,不再贅述。

    此外,history物件還有一個重要的state屬性,通過該屬性可以獲取使用pushState方法新增的實體物件的內容,即在使用pushState方法增加時data引數的實體值,它的呼叫格式如下。

    window.history.state;

    目前,各個瀏覽器對HTML5標準支援不全面,在使用history物件兩個新增的API方法時,首先需要檢測瀏覽器對它的支援狀態,檢測程式碼如下。

function supports_history_api(){
	return !!(window.history&&history.pushState);		
}
    呼叫上面的自定義函式,如果返回值為true,表示支援history物件新增的API方法,否則表示瀏覽器不支援history物件或新增的API方法。

    接下來通過一個簡單示例進行介紹。 
              示例 7-2 HTML5中history物件的方法

1.功能描述 

    在專案中新建兩個頁面7-2-a.html和7-2-b.html用於互訪。在第一個頁面中,新增兩個<div>和<span>元素,呼叫history物件的pushState方法時,分別顯示history物件當前新增的歷史記錄實體總量和內容;在第二個頁面中,同樣新增兩個<div>he  <span>元素,呼叫history物件的replaceState方法時,分別顯示history物件當前替換後的歷史記錄實體總量和內容。

檔案7-2-a.html內容

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
<style type="text/css">
	div{
		margin:5px 0px;
		font-size:13px;
	}
</style>
</head>
<body>
	<div>記錄總量:<span id="divNum"></span></div>
	<div>重新整理前:<span id="divShow"></span></div>
</body>
<script type="text/javascript">
	var obj_a_state = {
		Code:"10107",
		Name:"皮卡丘",
		Score:750
	};
	window.history.pushState(obj_a_state,"HTML5中history API方法","7-2-b.html");
	$("#divNum").html(history.length);
	$("#divShow").html(JSON.stringify(window.history.state));
</script>
</html>
檔案7-2-b.html內容

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
<style type="text/css">
	div{
		margin:5px 0px;
		font-size:13px;
	}
</style>
</head>
<body>
	<div>記錄總量:<span id="divNum"></span></div>
	<div>重新整理前:<span id="divShow"></span></div>
</body>
<script type="text/javascript">
	var obj_b_state = {
		Code:"10108",
		Name:"傑尼龜",
		Score:950
	};
	window.history.replaceState(obj_b_state,"HTML5中history API方法");
	$("#divNum").html(history.length);
	$("#divShow").html(JSON.stringify(window.history.state));
</script>
</html>

2.原始碼分析

    在7-2-a.html頁面的JavaScript程式碼中,首先定義一個名稱為“obj_a_state”的JSON格式物件,然後呼叫history物件中的pushState方法將該物件的內容插入當前歷史記錄的頂部,作為當前歷史記錄的實體物件。同時設定titile和url引數值,使頁面在無重新整理的情況下,動態將當前的地址修改為url引數傳來的地址。最後,將history物件的記錄總數和state屬性內容分別顯示在頁面指定的元素中。

    首次開啟7-2-a.html頁面後,雖然將當前頁的地址修改為url引數傳過來的地址,即將當前頁的地址修改為7-2-b.html,但僅限於修改,其實並沒有執行該地址。使用者此時單擊頁面導航條中的重新整理按鈕時,將真正瀏覽7-2-b.html頁。在該頁面的JavaScript程式碼中,呼叫history物件中的replaceState方法替換當前歷史記錄的物件,並將替換後的history物件記錄項總量和state屬性內容分別顯示在頁面指定的元素中。從上圖中可以看出,在記錄項總量未變的情況下,history物件state屬性內容發生了變化,說明替換成功。


7.1.3 location物件

    在瀏覽器視窗(window)中,location物件的功能是管理瀏覽器的地址。相對於history物件而言,該物件擁有更多實用的屬性和方法,如最常用的href屬性和reload方法,前者可以獲取當前瀏覽器的地址,後者方法可以重新按地址載入當前頁面。此外,還能通過呼叫location物件其他屬性,獲取瀏覽器地址的各個組成部分,其屬性與地址組成部分對應關係如下圖所示。


    從上圖可以看出,瀏覽器中URL地址的各個組成部分都可以通過呼叫location物件的屬性獲取,操作也十分方便。接下來通過一個簡單示例進行介紹。

              示例 7-3 location物件的屬性和方法

1.功能描述 

    在頁面中新增一個<div>元素,用於顯示遍歷location物件後獲取的物件方法和各個屬性值的內容。另外新增兩個按鈕元素,單擊“過載”按鈕時,重新載入當前頁面,單擊“替換”按鈕時, 在當前頁中開啟一個新的URL地址。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
</head>
<body>
	<div id="divShow"></div>
	<input id="btnreload" type="button" value="過載" onclick="window.location.reload();"/>
	<input id="btnreplace" type="button" value="替換" onclick="window.location.replace('http://www.baidu.com');"/>
</body>
<script type="text/javascript">
	var $HTML = "";
	var $obj_wl = window.location;
	for(var idx in $obj_wl){
		$HTML += "<b>" + idx + ":" + "</b>" + $obj_wl[idx] + "</br>";
	}
	$("#divShow").html($HTML);
</script>
</html>

2.原始碼分析

    在本示例的JavaScript程式碼中,為了將遍歷後的location物件各屬性名稱和值顯示在頁面元素中,首先定義一個名為$HTML的變數,用於儲存內容,然後定義一個名為“$obj_wl”的變數,用於儲存location物件。接下來使用for語句遍歷location物件,並獲取物件的每個屬性名稱和對應屬性值,將他們儲存在$HTML中。最後將變數$HTML的內容顯示在頁面元素中。

    在本示例的頁面程式碼中,新增兩個按鈕元素時就已繫結onclick事件。單擊“過載”按鈕時,將呼叫location物件的reload方法過載當前頁;單擊“替換”按鈕時,將呼叫location物件的replace方法,在當前頁面中開啟新頁面。

    location物件中的reload和replace方法雖然都是在當前頁面重新載入,但兩者有本質的區別。前者是重新載入當前頁的URL地址,即進行當前頁的重新整理;後者是先將當前頁的URL地址進行替換,再在當前頁中載入替換後的URL地址。

    此外,location物件中的hash屬性十分重要,該屬性易於設定和獲取,廣泛用於單頁應用中區域性效果的收藏,可以根據獲取的值執行不同的JavaScript程式碼,實現在單頁應用中無重新整理變換頁面的功能。該屬性也應用於下一節將要重點介紹的Backbone框架的導航器(router)中。


7.2 繫結導航地址

    通過前面章節的學習,使我們掌握了瀏覽器(window)中history、location物件屬性和方法的使用。在Backbone框架中構建router類時,大量封裝了這兩個物件中的屬性和方法,將頁面特定的url或hash屬性與定義好的action或event相繫結。在位址列瀏覽這些URL時,觸發繫結的action對應的函式或執行相應的事件,可以實現無重新整理載入新內容,以及收藏特定URL片段的功能,接下來詳細介紹該功能的實現過程。

7.2.1 action方式繫結URL地址

    所謂action方式,是將頁面特定的url或hash屬性與一個對應的函式(動作)相繫結,即在構建router類時,通過新增routes屬性,在該屬性中宣告url或hash屬性和函式的對應關係,這樣就完成了兩者間的繫結。一旦繫結完成,在瀏覽器中瀏覽對應的URL地址時,執行對應函式中的程式碼。接下來通過一個完整的示例進行介紹。

              示例 7-4 action方式繫結URL地址

1.功能描述 

    在頁面中新增兩個<div>元素,第一個元素用於建立導航條,在該元素中新增多個超級連結<a>元素。單擊某個連結時,進入相應的URL地址。同時在第二個<div>元素中顯示對應的功能說明和傳回的引數值。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="../script/jquery-1.11.1.js"></script>
<script src="../script/underscore.js"></script>
<script src="../script/backbone.js"></script>
</head>
<body>
	<div>
		<a href="">首頁</a> |		
		<a href="#search">查詢列表</a> |		
		<a href="#search/abc">關鍵字查詢</a> |		
		<a href="#search/abc/p2">頁碼關鍵字查詢</a> |		
		<a href="#error">其他頁</a> |		
	</div>
	<div id="divShow"></div>
</body>
<script type="text/javascript">
	var $divShow = $("#divShow");
	var testrouter = Backbone.Router.extend({
		routes:{
			"":"main",
			"search":"search_list",
			"search/:key":"search_key",
			"search/:key/p:page":"search_key_page",
			"*search":"search_default"
		},
		main:function(){
			$divShow.html("首頁");
		},
		search_list:function(){
			$divShow.html("查詢列表");
		},
		search_key:function(key){
			$divShow.html("查詢關鍵字為"+key+"記錄");
		},
		search_key_page:function(key,page){
			$divShow.html("查詢關鍵字為"+key+"記錄,頁碼為"+page);
		},
		search_default:function(){
			$divShow.html("其他頁");
		}
	});
	var router = new testrouter();
	Backbone.history.start();
</script>
</html>

2.原始碼分析

    在本示例的JavaScript程式碼中,首先定義一個名為“$divShow”的變數,用於儲存顯示內容的頁面元素。在構建router模組時新增routes屬性,並在該屬性值中宣告需要監聽的URL地址列表。以key/value的形式宣告,key表示URL地址中hash屬性的規則,而value則表示當在瀏覽器位址列中執行該規則宣告的URL時,需要執行的動作(action)函式名。

    然後,編寫URL地址中hash屬性規則執行的函式。在執行函式過程中,可以獲取URL地址中hash屬性規則傳來的實參值,並將該值顯示在頁面指定的元素中。

    最後,由於URL地址中hash屬性的導航功能是由router和history類共同完成的。前者用於宣告和解析導航規則,並繫結對應的動作(action)函式名,後者用於監聽已繫結的URL地址變化,並觸發已繫結的動作(action)。因此,需要先例項化一個router類的物件router,然後通過呼叫history物件中的start方法啟動對URL地址變化的監聽。



相關文章