Loading Backbone and Underscore using RequireJS

70,113

Solution 1

RequireJS 2.X now organically addresses non-AMD modules such as Backbone & Underscore much better, using the new shim configuration.

The shim configuration is simple to use: (1) one states the dependencies (deps), if any, (which may be from the paths configuration, or may be valid paths themselves). (2) (optionally) specify the global variable name from the file you're shimming, which should be exported to your module functions that require it. (If you don't specify the exports, then you'll need to just use the global, as nothing will get passed into your require/define functions.)

Here is a simple example usage of shim to load Backbone. It also adds an export for underscore, even though it doesn't have any dependencies.

require.config({
  shim: {
    underscore: {
      exports: '_'
    },
    backbone: {
      deps: ["underscore", "jquery"],
      exports: "Backbone"
    }
  }
});

//the "main" function to bootstrap your code
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone) {   // or, you could use these deps in a separate module using define

});

Note: this simplified code assumes that jquery, backbone and underscore are in files named "jquery.js", "backbone.js" and "underscore.js" in the same directory as this "main" code (which becomes the baseURL for require). If this isn't the case, you'll need to use a paths config.

I personally think with the built-in shim functionality, the advantages of not using a forked version of Backbone & Underscore outweigh the benefits of using the AMD fork recommended in the other popular answer, but either way works.

Solution 2

Update: As of version 1.3.0 Underscore removed AMD (RequireJS) support.

You can use the amdjs/Backbone 0.9.1 and the amdjs/Underscore 1.3.1 fork with AMD support from James Burke (the maintainer of RequireJS).

More info about AMD support for Underscore and Backbone.

// main.js using RequireJS 1.0.7
require.config({
    paths: {
        'jquery': 'libs/jquery/1.7.1/jquery',
        'underscore': 'libs/underscore/1.3.1-amdjs/underscore', // AMD support
        'backbone': 'libs/backbone/0.9.1-amdjs/backbone', // AMD support
        'templates': '../templates'
    }
});

require([
    'domReady', // optional, using RequireJS domReady plugin
    'app'
], function(domReady, app){
    domReady(function () {
        app.initialize();
    });
});

The modules are properly registered and there is no need for the order plugin:

// app.js
define([
    'jquery', 
    'underscore',
    'backbone'
], function($, _, Backbone){
    return {
        initialize: function(){
            // you can use $, _ or Backbone here
        }
    };
});

Underscore is actually optional, because Backbone now gets its dependencies on its own:

// app.js
define(['jquery', 'backbone'], function($, Backbone){
    return {
        initialize: function(){
            // you can use $ and Backbone here with
            // dependencies loaded i.e. Underscore
        }
    };
});

With some AMD sugar you could also write it like this:

define(function(require) {
    var Backbone = require('backbone'),
        $ = require('jquery');

    return {
        initialize: function(){
            // you can use $ and Backbone here with
            // dependencies loaded i.e. Underscore
        }
    };
});

Regarding the optimizer error: doublecheck your build configuration. I assume your path configuration is off. If you have a directory setup similar to the RequireJS Docs you can use:

// app.build.js
({
    appDir: "../",
    baseUrl: "js",
    dir: "../../ui-build",
    paths: {
        'jquery': 'libs/jquery/1.7.1/jquery',
        'underscore': 'libs/underscore/1.3.1-amdjs/underscore',
        'backbone': 'libs/backbone/0.9.1-amdjs/backbone',
        'templates': '../templates'
    }, 
    modules: [
        {
            name: "main"
        }
    ]
})

Solution 3

For reference, as of version 1.1.1 (~Feb '13), Backbone also registers itself as an AMD module. It will work with requirejs without the need to use its shim config. (James Burke's amdjs fork also hasn't been updated since 1.1.0)

Solution 4

Good news, Underscore 1.6.0 now supports requirejs define !!!

versions below this require shims, or requiring underscore.js then blindly hoping that the "_" global variable hasn;t been smashed (which to be fair is a fair bet)

simply load it in by

  requirejs.config({
    paths: {
        "underscore": "PATH/underscore-1.6.0.min",
    }
  });

Solution 5

I will write down directly, you can read the explaination on requirejs.org, you could use below code as a snippet for your everyday use; (p.s. i use yeoman) (since many things updated, im posting this as of Feb 2014.)

Make sure you included script in your index.html

<!-- build:js({app,.tmp}) scripts/main.js -->
<script data-main="scripts/main" src="bower_components/requirejs/require.js"></script>
<!-- endbuild -->

Then, in main.js

require.config({
    shim: {
        'backbone': {
            deps: ['../bower_components/underscore/underscore.js', 'jquery'],
            exports: 'Backbone'
        }
    },

    paths: {
        jquery: '../bower_components/jquery/jquery',
        backbone: '../bower_components/backbone/backbone'
    }
});

require(['views/app'], function(AppView){
    new AppView();
});

app.js

/**
 * App View
 */
define(['backbone', 'router'], function(Backbone, MainRouter) {
    var AppView = Backbone.View.extend({
        el: 'body',

        initialize: function() {
            App.Router = new MainRouter();
            Backbone.history.start();
        }
    });

    return AppView;
});

I hope I was useful.!

Share:
70,113

Related videos on Youtube

Aaronius
Author by

Aaronius

Updated on January 19, 2020

Comments

  • Aaronius
    Aaronius over 4 years

    I'm trying to load Backbone and Underscore (as well as jQuery) with RequireJS. With the latest versions of Backbone and Underscore, it seems kind of tricky. For one, Underscore automatically registers itself as a module, but Backbone assumes Underscore is available globally. I should also note that Backbone doesn't seem to register itself as a module which makes it kind of inconsistent with the other libs. This is the best main.js I could come up with that works:

    require(
    {
        paths: {
            'backbone': 'libs/backbone/backbone-require',
            'templates': '../templates'
        }
    },
    [
        // jQuery registers itself as a module.
        'http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js',
    
        // Underscore registers itself as a module.
        'http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.1/underscore-min.js'
    ], function() {
    
        // These nested require() calls are just due to how Backbone is built.  Underscore basically says if require()
        // is available then it will automatically register an "underscore" module, but it won't register underscore
        // as a global "_".  However, Backbone expects Underscore to be a global variable.  To make this work, we require
        // the Underscore module after it's been defined from within Underscore and set it as a global variable for
        // Backbone's sake.  Hopefully Backbone will soon be able to use the Underscore module directly instead of
        // assuming it's global.
        require(['underscore'], function(_) {
            window._ = _;
        });
    
        require([
            'order!http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js',
            'order!app'
        ], function(a, app) {
            app.initialize();
        })
    });
    

    I should mention that, while it works, the optimizer chokes on it. I receive the following:

    Tracing dependencies for: main
    js: "/home/httpd/aahardy/requirejs/r.js", line 7619: exception from uncaught JavaScript throw: Error: Error: Error evaluating module "undefined" at location "/home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js":
    JavaException: java.io.FileNotFoundException: /home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js (No such file or directory)
    fileName:/home/httpd/aahardy/phoenix/trunk/ui/js/../../ui-build/js/underscore.js
    lineNumber: undefined
    http://requirejs.org/docs/errors.html#defineerror
    In module tree:
        main
    

    Is there a better way of handling this? Thanks!

    • kaha
      kaha over 12 years
      Did you do it using any tutorial?
    • Aaronius
      Aaronius over 12 years
      I looked through various tutorials like backbonetutorials.com/organizing-backbone-using-modules but they seem to be outdated now with the latest versions of underscore and backbone.
    • Georgi-it
      Georgi-it over 9 years
      I also found requirejs hard to use with other libraries and vice-versa. That is why I created a library which is much easier to use and is tested with angular. There is a demo application at the bottom: gngeorgiev.github.io/Modulerr.js You can also combine all scripts into one without the dependency to Modulerr.js
    • Pavel 'Strajk' Dolecek
      Pavel 'Strajk' Dolecek over 9 years
      btw Synchronous Asynchronous Module Definition is kinda oxymoron :)
    • Aaronius
      Aaronius over 9 years
      Ha! Good point. Edited.
  • Aaronius
    Aaronius over 12 years
    That's exactly what I was looking for. Thank you! Great detailed answer as well. It's now running just as you've described.
  • Robert Guest
    Robert Guest over 12 years
    +1 accurate, working and updated answer+examples. excellent job Riebel, you've help me, and i'm sure others, a lot.
  • Aaronius
    Aaronius over 12 years
    Super-bonus for keeping this updated long after the original post.
  • txominpelu
    txominpelu about 12 years
    Great answer @Riebel ! It's been really useful to me. Btw, I would also recommend to have a look at volo. It's a library created by jrburke (the creator of requirejs) to retrieve dependencies from github. For example retrieving the amd version of underscore is done just typing: volo add underscore
  • Henry
    Henry almost 12 years
    Should this code be use with Sample RequireJS 2.0.1 + jQuery 1.7.2 project requirejs.org/docs/download.html#samplejquery?
  • B Robster
    B Robster almost 12 years
    If I understand you correctly, Henry, you're asking whether shim is necessary for $ plugins. It isn't, IF you use the combined require-jquery.js file from that sample project. That is because with the combined file, jquery gets synchronously loaded with require, so jquery is guaranteed to be loaded by the time you try to use any $ plugins in any module. In this case, when you want to use $ plugins, you can just include them in your dependency list as if they were AMD, even if they're not. This is definitely an exception to the rule, and generally you'll need shim for any non-AMD modules.
  • B Robster
    B Robster almost 12 years
    Note that shim cofiguration is compatible with that sample project and could be used to add other non-AMD libraries.
  • koblas
    koblas almost 12 years
    Just thought I would mention that this is really the way to go, wish I could give +50 upvotes to get it to be #1 answer.
  • Rob W
    Rob W over 11 years
    The method in this answer looked promising, but didn't work for me. I used gist.github.com/2517531 instead, which worked fine.
  • B Robster
    B Robster over 11 years
    Rob W- sorry that didn't work for you. It may be that the paths don't match your directory structure or something. The code is correct--in fact its not substantively different than the shim code right out of the require.js docs, which shows how to load Backbone: requirejs.org/docs/api.html#config-shim -- so there must be other factors at play. The gist you linked to is just an AMD-ized copy of backbone. Since you're going that route, you should probably use the forks linked to in the accepted answer above. Good luck!
  • eipark
    eipark over 11 years
    I'm having issues with this as well. It seems that Backbone is not defined when I load my site, but only sometimes. It seems consistent with the fact that the dependencies are getting loaded asynchronously/non-deterministically, but I copied your shim code verbatim. Screenshot of some errors: cl.ly/image/320C1t3d453R. I then tried using require-jquery.js but it doesn't seem to be getting jQuery properly. Any insights?
  • B Robster
    B Robster over 11 years
    I edited the code to simplify, in case the example 'paths' configuration i had in there was throwing people off. So, see the note. If you are bootstrapping your code from a single "main" module, which is passed to require.js as data-main, and you don't have any inline code or are trying to use any other javascript that depends on backbone or underscore outside the flow from your main.js code, you should be fine.
  • Dwight Spencer
    Dwight Spencer almost 9 years
    More usefull than you would know. This is exactly what I've been attempted to build out on a project of mine, bower_components and all. Thanks @STEEL