一個相似backbone路由的純淨route ( 前端路由 客戶端路由 backbone路由 )

你們用backbone、angular,可能都習慣了內置的路由,這兩個框架的路由都是很是優秀的,強大而簡單。git

客戶端(瀏覽器)路由原理其實比較簡單,其實就是監聽hash的變化。github

在以前的架構探討中,說到director.js這個路由類庫很差使,那麼,在這一篇,咱們嘗試自行實現一個簡潔並且很是好使的路由類庫。正則表達式

 

原理先介紹,無非幾個步驟:瀏覽器

  • 創建配置表(字符串路徑和函數的映射)
  • 監聽路由(onhashchange)
  • 處理路由變化,跟配置表的路徑作匹配
    • 路徑轉化爲正則表達式
    • 正則exec,匹配+抽取參數

 

其中難點就在於路徑轉化爲正則表達式,director沒作好就是這一步,而backbone則作得很是很是強大,那麼咱們能夠嘗試把backbone這一塊代碼摳出來。架構

 

路由表:app

    var Route = root.Route = {
        init: function (map) {
            var defaultAction = map['*'];
            if(defaultAction){
                Route.defaultAction = defaultAction;
                delete map['*'];
            }
            Route.routes = map;
            init();
            onchange();
        },
        routes: {},
        defaultAction: null
    };

 

監聽路由變化:框架

    /**
     * 這段判斷,引用於director:https://github.com/flatiron/director
     */
    function init(){
        if ('onhashchange' in window && (document.documentMode === undefined
            || document.documentMode > 7)) {
            // At least for now HTML5 history is available for 'modern' browsers only
            if (this.history === true) {
                // There is an old bug in Chrome that causes onpopstate to fire even
                // upon initial page load. Since the handler is run manually in init(),
                // this would cause Chrome to run it twise. Currently the only
                // workaround seems to be to set the handler after the initial page load
                // http://code.google.com/p/chromium/issues/detail?id=63040
                setTimeout(function() {
                    window.onpopstate = onchange;
                }, 500);
            }
            else {
                window.onhashchange = onchange;
            }
            this.mode = 'modern';
        } else {
            throw new Error('sorry, your browser doesn\'t support route');
        }
    }

 

處理路由變化,先拼湊正則表達式:函數

    /**
     * 引自backbone,很是牛逼的正則
     * @param route
     * @returns {RegExp}
     */
    function getRegExp(route){
        var optionalParam = /\((.*?)\)/g;
        var namedParam    = /(\(\?)?:\w+/g;
        var splatParam    = /\*\w+/g;
        var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
        route = route.replace(escapeRegExp, '\\$&')
            .replace(optionalParam, '(?:$1)?')
            .replace(namedParam, function(match, optional) {
                return optional ? match : '([^/?]+)';
            })
            .replace(splatParam, '([^?]*?)');
        return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
    }

從原來的:module2/:name變成標準的正則表達式,箇中奧妙你們自行頓悟測試

 

循環匹配:this

    function onchange(onChangeEvent){
        var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
        var url = newURL.replace(/.*#/, '');
        var found = false;
        for (var path in Route.routes) {
            var reg = getRegExp(path);
            var result = reg.exec(url);
            if(result && result[0] && result[0] != ''){
                var handler = Route.routes[path];
                handler && handler.apply(null, result.slice(1));
                found = true;
            }
        }
        if(!found && Route.defaultAction){
            Route.defaultAction();
        }
    }

 

而後。。。作個簡單的測試:

<script src="libs/backbone-route.js"></script>
<script>

    Route.init({
        'module1': function(){
            console.log(1);
        },
        'module2/:name/:age': function(){
            console.log(2, arguments);
        },
        'module3(/:name)(/:age)': function(){
            console.log('3', arguments);
        },
        '*': function(){
            console.log(404);
        }
    });
</script>

 

 

本文代碼:https://github.com/kenkozheng/HTML5_research/tree/master/backbone-route

相關文章
相關標籤/搜索