Backbone.js routing without changing url

2020-05-23 08:23发布

问题:

I am migrating a single-page web application based on Backbone.js and jQuery to a Chrome extension. However, neither the pushState nor the hashbang-based router modes seem to play well with the environment within the extension. I've come to the conclusion that I'm better off just directly rendering views on user interactions, bypassing the window.location system altogether. However, I'm not too sure how to implement this without changing calls to Router.navigate in dozens of files.

Is there a pluggable/modular way to keep the Backbone routing system but bypass any changes to the url?

回答1:

I you really want to stick with using Router.navigate to benefit from the routing system that Backbone.js provides without having to deal with hashbang bugs when used in a Chrome extension (e.g. routes including a slash being overwritten), you could make Router.navigate load the url directly, skipping the whole pushState gymnastic.

This is actually pretty easy to accomplish:

Router = Backbone.Router.extend({

  navigate: function (url) {

    // Override pushstate and load url directly
    Backbone.history.loadUrl(url);

  },

  // Put routes here
  routes: { }

});

You can then call Router.navigate(url) to load a new route without changing history, or even bind the action to every link containing a data-backbone attribute (e.g. <a href="login" data-backbone>Login</a>) with an event like this:

$(function(){

  // Initialize router
  Router = new Router;
  Backbone.history.start();

  // Bind a[data-backbone] to router
  $(document).on('click', 'a[data-backbone]', function(e){
    e.preventDefault();

    Router.navigate( $(this).attr('href') );
  });

});


回答2:

You could redefine what Router.navigate does, but you're better off just not using the Background.router altogether. I think it could create some confusion, and your code will be cleaner without it if you're currently triggering history changes from views.

Backbone.Marionette has a concept of Controllers, which work a lot like routers without a URL-map (with Marionette, the idea is to keep your route definitions minimal, and call controllers for the behavior instead). You also don't need to use any of the components of Marionette you don't want.

If you really wanted to stick with the router as it is, you could probably just redefine Backbone.History.navigate to (note, untested)

navigate: function(fragment, options) {
    if (!History.started) return false;
    if (!options || options === true) options = {trigger: options};
    fragment = this.getFragment(fragment || '');
    if (this.fragment === fragment) return;
    this.fragment = fragment;     
    if (options.trigger) this.loadUrl(fragment);
}