本章將介紹如何使用avalon來實現前端路由功能。
我們需要用到兩個avalon路由配套模塊—— mmHistory.js 和 mmRouter.js 。其中mmHistory是用于歷史管理,它會劫持頁面上所有點擊鏈接的行為,當這些鏈接是以 #/ 、#!/ 開頭,就嘗試匹配路由規則,阻止頁面刷新(通過hash方式或HTML5的replaceState方式)。mmRouter是給我們定義路由規則,路由規則可以更精細地指定每個參數(param)的匹配規則,如果符合就執行對應的回調,如果不符合,就進入error回調。
關于該路由系統更具體的描述,可以查閱這里。
作為示例,我們打算制作一個網站的 “用戶中心” 頁面,其中左側為導航列表,右側為受左側列表控制的內容顯示區域:
該“用戶中心”頁面有這么幾個要求:
⑴ 頁面不跳轉,僅做局部(即內容區域部分)刷新;
⑵ 可以通過不同的url進入對應的頁面(即內容區域顯示對應的內容);
⑶ 瀏覽器能記住url狀態,比如從“賬戶詳情”點入“我要充值”頁面,然后再點擊瀏覽器返回按鈕,可以正確回到“賬戶詳情”頁面。
由于不是石器時代,自然不會再選擇iframe這種內耗高、不友好的元素來架構頁面(而且iframe也實現不了后面兩個需求呀)。那么我們會很快聯想到Ajax技術,這個想法很本質,不過單純的Ajax也沒辦法達到我們的要求,所以才需要引入開頭提到的兩個avalon路由模塊。
我們可以先寫出簡單的頁面原型:
index.html:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>賬戶中心</title> <link rel="stylesheet" href="css/user.css"> <script src="js/lib/require.js" type="text/javascript" data-main="js/page/user"></script> </head> <body ms-controller="user" class="ms-controller"> <script type="text/javascript"> //這里給后端提供數據接口 var conf = { username: {"id": "11", "name": "VaJoy"} } </script> <header> <span>{{username.name}}你好,歡迎來到賬戶中心</span> </header> <nav> <ul> <li><a href="#!/index">我的首頁</a></li> <li><a href="#!/detail">賬戶詳情</a></li> <li><a href="#!/recharge">我要充值</a></li> </ul> </nav> <article> 內容... </article> </body> </html>
user.js:
require.config({ baseUrl: 'js/lib/', paths:{ avalon: 'avalon', domReady:'domReady', mmHistory: 'mmHistory', mmRouter: 'mmRouter', jquery: 'jq' }, shim:{ avalon: { exports: "avalon" }, mmHistory:{ deps: ['avalon']}, mmRouter:{ deps: ['avalon']} } }); require(['avalon',"domReady!"], function() { var vm = avalon.define({ $id: "user", username:conf.username }); avalon.scan(); });
user.css:
body,html{padding: 0;margin:0;background: #EEE;} .ms-controller{visibility: hidden;} header{height: 50px;background: white;} header>span{display:block;padding: 16px;} nav{position: absolute;left:0;margin-top:50px;width: 200px;} nav>ul>li{margin-top: 12px;} nav>ul>li>a{text-decoration: none;color:blue;} nav>ul>li>a:hover{color:red;} article{padding: 15px;margin-left:200px;min-height: 600px;background: white;}
運行結果如下:
接著我們要新建三個頁面——mine.html、detail.html 和 recharge.html ,分別對應“我的首頁”、“賬戶詳情” 和 “我要充值” 的右側內容,咱在里面隨便寫點內容意思意思即可,比如mine.html我就寫了一句話:
接著我們默認先把mine.html引入到index.html中,這里我們借助avalon的 ms-include-src 接口,修改下index.html:
<nav> <ul> <li><a href="#!/index">我的首頁</a></li> <li><a href="#!/detail">賬戶詳情</a></li> <li><a href="#!/recharge">我要充值</a></li> </ul> </nav> <article ms-include-src="pageUrl"> <!--這里使用ms-include-src接口,它會引入pageUrl屬性所對應的文件--> </article>
接著修改 user.js的部分:
require(['avalon',"domReady!"], function() { var vm = avalon.define({ $id: "user", username:conf.username, pageUrl:"mine.html" //默認為mine.html }); avalon.scan(); });
運行如下:
接著是時候讓 mmHistory.js 和 mmRouter.js 發揮它們的作用了,我們修改下user.js的部分代碼:
require(['mmHistory','mmRouter',"domReady!"], function() { var vm = avalon.define({ $id: "user", username:conf.username, pageUrl:"mine.html" //默認為mine.html }); function callback() { if(this.path==="/index"){ vm.pageUrl="mine.html"; }else { var path_tail = this.path.replace(/\//, ""); vm.pageUrl = path_tail + ".html"; //動態修改pageUrl屬性值 } } avalon.router.get("/*path", callback); //劫持url hash并觸發回調 avalon.history.start(); //歷史記錄堆棧管理 avalon.scan(); });
注意由于在 require.config 的 shim 中我們已經定義了 mmHistory.js 和 mmRouter.js 是依賴于avalon的,故此處無須再引入avalon模塊,requireJS執行該代碼段之前會先加載好avalon的。
我們通過這兩行代碼執行了路由和歷史記錄的管理:
avalon.router.get("/*path", callback); //劫持url hash并觸發回調 avalon.history.start(); //歷史記錄堆棧管理
其中router.get() 的第一個參數表示路由匹配規則,比如這里的“/*path”表示匹配全部路徑,匹配到了就觸發回調callback函數。
更多的匹配規則我們可以直接在 mmRouter.js 中查看注釋信息:
router.get() 在觸發callback前會生成一個this.path屬性供callback調用(你也可以給回調函數定義一個參數,其默認值等同與path),其值為當前匹配到的路徑,比如當url后綴變成 #!/recharge 的時候,this.path的值為匹配到的"/recharge" 。了解了這個之后,callback 函數也很好理解了:
function callback() { if(this.path==="/index"){ vm.pageUrl="mine.html"; //如果url后綴變成"#!/index",則pageUrl為“mine.html” }else { var path_tail = this.path.replace(/\//, ""); //去掉this.path值的第一個斜杠 vm.pageUrl = path_tail + ".html"; //動態修改pageUrl屬性值 } }
這時候的運行結果如下所示:
自此便實現了我們的需求。但是這樣還不夠完美——每個頁面的樣式咋處理呢?
我們可以直接在頁面上寫<style>標簽,或者直接寫個<link>引入外部樣式文件,但前者不好維護,后者畢竟不是插入到head中的不太規范。那么我們能否也用requireJS模塊化動態引入樣式文件呢?答案是肯定的,不過得借助于其組件css.js。
以“賬戶詳情”(detail.html)為例,我們創建一個detail.css文件,里面設置 .detail{color:red;}。
先確保require.config中的paths里加上了該組件:
paths:{ //這里配置的地址,都是相對于上方的baseUrl的 avalon: 'avalon', domReady:'domReady', mmHistory: 'mmHistory', mmRouter: 'mmRouter', css: 'css' //加上css.js }
然后修改detail.html頁面內容:
<section ms-controller="detail" class="detail ms-controller"> 喲喲喲,這里是詳情頁面,{{username.name}}你好 </section> <script> require(['avalon','css!../../css/detail.css'], function(){ //下面的其實建議寫成一個模塊detail.js然后由require引入 avalon.define({ $id: "detail", username: conf.username }); avalon.scan(); }) </script>
“css!/XXX.css” 是css.js的寫法,注意以"css!"開頭即可。
運行結果如下:
以上便是avalon前端路由的簡單實現,本章的示例代碼可以從這里下載。
后續章節可能會開始寫一寫avalon的API。共勉~
文章列表