Require.js module not seeing Backbone Router.js

2019-04-17 06:26发布

问题:

In this simple Require/Backbone app

https://github.com/thisishardcoded/require-prob

Why does app.js see Router but TestView.js not?

Here is the first line of app.js

define(['router'],function (Router) {

and here is the first line of TestView.js

define(['backbone','router'],function(Backbone,Router){

Check out the repo for full details, download, run and check console log if you feel so inclined

Thanks! Jim

More: Ok, the answer is - because of the order in which it is loaded and even if that were altered I have a circular dependency don't I? TestView needs Router, Router needs TestView.

In which case the solution might be

var r=require('router);
r.navigate or whatever

but, that seems like a shame that Router is not directly accessible everywhere and, is the above method good practice anyway?

回答1:

Surely it happens because of circular dependency. To resolve it, you either pass router to view's constructor and remove router dependency from view's module, or use require('router') in your view.

1st option, router.js:

app_router.on('route:test', function () {
    var testView = new TestView({router: app_router});
    testView.render();
})

1st option, view.js:

define(['backbone'], function(Backbone){

    var TestView=Backbone.View.extend({
        el: '#test',
        initialize: function() {
            // get router from constructior options
            this.router = this.options.router
        },
        render: function(){
            this.$el.html('<p>Foo!</p>');
            console.log("TestView.js does not find 'Router',", this.router);
        }
    });

    return TestView;

});

2nd option, view.js:

define(['backbone','router'], function(Backbone, router){

    // at this point router module is not loaded yet so router is undefined

    var TestView=Backbone.View.extend({
        el: '#test',
        initialize: function() {
            // at this point router module is loaded and you may access it with require call
            this.router = require('router');
        },
        render: function(){
            this.$el.html('<p>Foo!</p>');
            console.log("TestView.js does not find 'Router',", this.router);
        }
    });

    return TestView;

});

2nd option is also described here: http://requirejs.org/docs/api.html#circular



回答2:

You should define baseUrl property in your main.js file that contains RequireJS config. In this way all paths to modules in your application will be relative to that baseUrl.

See:

http://requirejs.org/docs/api.html#jsfiles
http://requirejs.org/docs/api.html#config-baseUrl



回答3:

I downloaded and inspected your code. Following could be the issues:

  1. require.js only works with AMDs. Since backbone no longer supports AMD. You will need to use AMD enabled version of Backbone. You can get it here

  2. TestView is the dependency in you Router. So it loads before the Router is loaded.

You might want to improve the coding pattern. Here is my suggestion:

App.js

define([

'backbone', 'router', ], function(Backbone, MainRouter){ 'use strict';

var AppView = Backbone.View.extend({

    initialize: function(){

        App.router = new MainRouter();
        Backbone.history.start();
    }
});

return AppView;
});

Router.js

define([
  'backbone',
  'view/TestView'
], function(Backbone, TestView){
    var Main = Backbone.Router.extend({
        routes: {
            'test': 'test'
        },

        test: function(){
            new TestView({
                // pass model or collection to the view
                // model: new TestModel // remember to require
            });
        }

    });

    return Main;
});

EDIT Listening to events:

// in main.js
var window.Vent = {};

_.extend(window.Vent, Backbone.Events);

// now in any view you can trigger a event
$('something').on('click', function(){
    window.Vent.trigger('somethinghappened', this); 
    // this is reference to current object
});

// now in other view you can do
window.Vent.on('somethinghappened', this.run, this); 
// this in the end is the reference we passed when event was triggered

run: function(obj){
    //this function will run when the event is triggered
    // obj is the object who triggered the event
}

PS: why do you want to use router in view?? I have built quite a few backbone apps. Never needed to do so.



回答4:

You can use available Backbone.history.navigate to achieve your goal easier, because Router.navigate is a simple wrapper for it. Consider this part of Backbone source:

navigate: function(fragment, options) {
  Backbone.history.navigate(fragment, options);
  return this;
},