在Backbone中添加添加剂路线的良好模式

时间:2013-05-21 22:42:25

标签: backbone.js architecture routing marionette backbone-routing

我已经看过像BackboneMVC和Backbone Marionette那样的子路由。 我想我正在寻找一些不同的东西。 有没有人提出好的模式,而不是子路由,而是添加路线?

比如说,你有一个可以在任何屏幕上显示的大型配置文件灯箱。您想将其添加到浏览器历史记录中,并让网址能够重新创建它。所以你可能有这些网址:

'dashboard/lightbox/profile'
'game/3/lightbox/profile'

在第一个路线中,它应该执行dashboard路线的所有行为,然后应用添加剂lightbox/profile路线的行为。这样,灯箱打开,仪表板在后台。 在第二种情况下,它应该类似地执行game/3路线的所有行为,然后打开灯箱。

这是任何人都听说过或实施的模式吗? 如果不使用splats,我想不出这样做的方法,比如:

routes: {
  'dashboard/*path': 'showDashboard',
  'game/:id/*path': 'showGame'
},
showDashboard: function(path) {
  //Show the dash
  this._checkIfLightboxShouldOpen(path)
},
showGame: function(id, path) {
  //Show the correct game based on id
  this._checkIfLightboxShouldOpen(path)
},
_checkIfLightboxShouldOpen(path) {
  // Parse the path string for lightbox behaviors
}

有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

我最近的项目需要这个,我打算在某些时候将这段代码作为开源发布,但你可以这样做:

创建一个全局路由器来处理所有路由:

App.GlobalRouter = Backbone.Router.extend({
    initialize: function(){
        this._routes = {};
    },

    registerRoute: function(route, rootRoute){
    var rootName;

    if(rootRoute) {
        route = rootRoute + '/' + route;
        rootName = this.registerRoute(rootRoute);
    }

    if(!_.isRegExp(route))
        route = this._routeToRegExp(route);

        var name = this._routes[route] ? this._routes[route] : _.uniqueId('r');
        this._routes[route] = name;

        this.route(route, name, function(){});

        if(rootName) {
            this.on('route:'+name, function(){
                var args = slice(arguments);
                this.trigger.apply(this, ['route:' + rootName].concat(args));
            }.bind(this));
        }

        return name;
    }
});

然后创建一个:

App.globalRouter = new App.GlobalRouter();

然后创建一个修改过的路由器以扩展所有路由器:

App.Router = Backbone.Router.extend({
    constructor: function (options){
        options = options || {};
        if(options.root) this.root = options.root;
        this.globalRouter = App.globalRouter;

        Backbone.Router.apply(this, [options]);
    },

    route: function(route, name, callback, root){
        if(!App.globalRouter) return false;

        // If callback is root param
        if(callback && !_.isFunction(callback)) {
            root = callback;
            callback = null;
        }

        // If no name is callback param.
        if(_.isFunction(name)) {
            callback = name;
            name = '';
        }

        if(!callback)
            callback = this[name];

        var router = this;
        var roots = root || this.root;
        if(roots && !_.isArray(roots)) roots = [roots];

        if(roots) {
            _.each(roots, function(root){
                var globalName = App.globalRouter.registerRoute(route, root);
                router.listenTo(App.globalRouter, 'route:'+globalName, function(){
                    var args = slice(arguments);
                    var callbackArgs = args.slice(callback && -callback.length || 0);
                    callback && callback.apply(router, callbackArgs);
                    router.trigger.apply(router, ['route:' + name].concat(callbackArgs));
                    router.trigger('route', name, callbackArgs);
                });
            });
        } else {
            var globalName = App.globalRouter.registerRoute(route);
            router.listenTo(App.globalRouter, 'route:'+globalName, function(){
                var args = slice(arguments);
                var callbackArgs = args.slice(callback && -callback.length || 0);
                callback && callback.apply(router, callbackArgs);
                router.trigger.apply(router, ['route:'+name].concat(callbackArgs));
                router.trigger('route', name, callbackArgs);
            });
        }

        return this;
    }
});

从这里你可以创建所需数量的路由器并在同一路由上注册它们,你也可以创建一个路由器来监听路由,所以在你的情况下你可能有2或3个路由器,这里是你可以做的一个例子:

var defaultRouter = App.Router.extend({
   routes: {
       'dashboard': 'showDashboard',
       'game/:id': 'showGame'
   },
   showDashboard: function() {},
   showGame: function(id) {},
});

var profilerRouter = App.Router.extend({
   root: [
       'dashboard',
       'game/:id'
   ],
   routes: {'profile', 'showProfile'},
   showProfile: function(){//Show lightbox}
});

这会听取/dashboard/game/:id并在正在收听的defaultRouter上调用该功能。然后,如果/profile位于URL的末尾,则任何一个路由都将捕获该值并在profileRouter上运行showProfile函数。

注意:我已经快速修改了我的项目代码,以更改一些名称/名称空间问题,因此您可能需要检查我没有遗漏任何内容,但代码应该正确

更新示例:

  • 如果用户导航至/game/:id,则会使用参数defaultRouter > showGame致电:id
  • 如果用户导航至/game/:id/profile,则会使用参数defaultRouter > showGame呼叫:id。它也会调用profileRouter > showProfile但没有参数(即它不会从/ game /:id root发送:id)。