一个类似backbone路由的纯净route ( 前端路由 客户端路由 backbone路由 )

大家用backbone、angular,可能都习惯了内置的路由,这两个框架的路由都是非常优秀的,强大而简单。

客户端(浏览器)路由原理其实比较简单,其实就是监听hash的变化。

在之前的架构探讨中,说到director.js这个路由类库不好使,那么,在这一篇,我们尝试自行实现一个简洁而且非常好使的路由类库。

 

原理先介绍,无非几个步骤:

  • 建立配置表(字符串路径和函数的映射)
  • 监听路由(onhashchange)
  • 处理路由变化,跟配置表的路径做匹配
    • 路径转化为正则表达式
    • 正则exec,匹配+抽取参数

 

其中难点就在于路径转化为正则表达式,director没做好就是这一步,而backbone则做得非常非常强大,那么我们可以尝试把backbone这一块代码抠出来。

 

路由表:

    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变成标准的正则表达式,个中奥妙大家自行顿悟

 

循环匹配:

    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

posted @ 2015-07-16 12:41  拂晓风起-Kenko  阅读(3409)  评论(0编辑  收藏  举报