文章出處

本章將介紹如何使用avalon來實現前端路由功能。

我們需要用到兩個avalon路由配套模塊—— mmHistory.jsmmRouter.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。共勉~

donate


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()