In this post we are going to learn how to use Require.js with Backbone.js and Underscore.js
This post build on the Re-Learning Backbone.js series.
As usual, the examples in this tutorial are extremely simple. We have one goal here and that is to load Underscore.js and Backbone.js using Require.js
We are going to start out with an example that doesn’t function correctly. Don’t worry, I believe it’s important to show you the evolution of creating an application from the very beginning to a working version. We will take very small steps to get where we need to go.
Here are the libraries and their version that we will use in this post:
- jQuery – version: 1.8.3
- RequireJS – version: 2.1.2
- Backbone.js – version: 0.9.9
- Underscore.js – version 1.4.3
Here’s the source code for the example below: Source
Getting Started
To get started we need a structure for our website such as this:
We also need the following libraries jQuery, Backbone.js, Underscore.js and Require.js. These libraries should be stored in the “libs” directory.
First create two files. The first file will be called Require1.html and this file will be in the root directory. Add the following code to the file.
Here’s the code for Require1.html
<html > <head> <title></title> </head> <body> <script data-main="scripts/main1" src="scripts/libs/require.js"></script> </body> </html>
All the HTML file does is tell RequireJS to execute the main.js file in the script directory.
The second file will be called main1.js and this file will be in the “scripts” directory. Add the following code to the file.
requirejs.config({ urlArgs: "bust=" + (new Date()).getTime(), //Remove for prod paths: { "jquery": "libs/jquery-1.8.3", "underscore": "libs/underscore", "backbone": "libs/backbone" }, }); require(["jquery", "underscore", "backbone"], function ($, _, Backbone) { console.log("Test output"); console.log("$: " + typeof $); console.log("_: " + typeof _); console.log("Backbone: " + typeof Backbone); } );
The main1.js file has two parts, the config method and the require method. The config method is used to setup RequireJs. The config is not mandatory, but it does simplify the code. In the config we included only the “paths” option, but there are many other options available. To see a list of options go to RequireJs Config Options. In the paths option we identify the modules that will be needed. Each module file is in the “scripts/libs” directory. You will notice that each JavaScript file that is referenced does not include the “.js” extension. RequireJS assumes that all files are scripts and the “.js” is not needed. The order of the values in the config paths is not important; the files can be in any order. If we wanted we could have put backbone first.
jQuery is a named module and with named modules we have to use the name identified in the module. In our example we have the code "jquery": "libs/jquery-1.8.3"
. The name “jquery” is required; the name cannot be anything else. jQuery is AMD compliant. Backbone.js and Underscore.js are not by default AMD compliant. In the jQuery file at the very end there is the following code. This code is there to support AMD loaders such as RequireJS.
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } ); }
For more information on AMD see: AMD
I was not aware of named modules when I originally started using RequireJS, this caused me headaches. Hopefully by point this out here, I will help you reduce any future pain. Not all AMD modules are named.
In the require method there is an array of strings. These strings maps to the strings in the config path; as you can see the name values are the same. In the “require” method the “function” has attributes, these attributes map in order to the values in the string array. So “$” maps to “jquery”, “underscore” maps to “_”, and “backbone” maps to “Backbone”. As you can tell the names are not the same, so we can change the variable names to whatever we want.
Finally we log the values of the function attributes.
In this tutorial no response will be rendered in the browser. To do testing I will use Google Chrome Developer Tools.
With the Developer Tools enabled, load the page. Select the “Network” tab and display the console (see image above).
Since the Backbone.js and Underscore.js modules are not AMD compatible we receive an error. Also since these modules are not compatible the values that are logged to the console are “undefined”. As you can see, the jQuery ($) value that was logged to the console was “function”; this is what we expected. We will fix these other issues soon.
************************************************************
Better Errors
In the next few sections we are going to make very simple changes and view the results of these changes.
Lets change the main.js file just a tiny bit.
All we did here was add the config option “enforceDefine” and set it to to true.
enforceDefine: If set to true, an error will be thrown if a script loads that does not call define() or have a shim exports string value that can be checked. See Catching load failures in IE for more information. RequireJS enforceDefine
As we can see, since Backbone.js and Underscore.js does not support AMD and does not use the “define” method, errors are thrown. This is good, I don’t want to write validation code for every module that is loaded.
There is something odd here. The first error identifies that main2.js does not have a define, which it doesn’t. This has been identified as an issue: enforceDefine triggers error on main file. For the most part the arthur (James Burke) of RequireJS mentions to ignore the error or use “define” method instead of the require method. So in the next example we are going to change the “require” method to “define”. It seems like an odd workaround, but this is what we will do.
***************************************************
Change Require to Define
I would like to see valid errors, so lets change “require” to “define”
Since we change “require” to “define”, the error “Uncaught Error: No define call for main2” is no longer raised.
Now that we have the configuration setup, lets get Backbone.js and Underscore.js working.
************************************************************************
Using RequireJS Shims
There are two way to get Backbone.js and Underscore.js work with RequireJS; Shims and AMD. In this first example we will use Shims.
So lets add a shim.
requirejs.config({ enforceDefine: true, paths: { "jquery": "libs/jquery-1.8.3", "underscore": "libs/underscore", "backbone": "libs/backbone" }, shim: { "underscore": { deps: [], exports: "_" }, "backbone": { deps: ["jquery", "underscore"], exports: "Backbone" }, } }); define(["jquery", "underscore", "backbone"], function ($, _, Backbone) { console.log("Test output"); console.log("$: " + typeof $); console.log("_: " + typeof _); console.log("Backbone: " + typeof Backbone); } );
In the above code we added a “shim” option to the config.
The shim for underscore does not have any dependencies, so we leave the “deps” array empty. The property for export is set to “_”, this is required for Underscore.js.
The shim for backbone has two dependencies, so we set “deps” array to include “jquery” and “underscore”. The property for export is set to “Backbone”, this is required for Backbone.js. Notice that the capitalization for “Backbone”; this stumped me for a good while, so make sure the capitalization is correct.
It’s important that the “exports” property is the same as the global variable name for the component that you are referencing; capitalization does matter. For example Backbone.js declares a global variable “Backbone” and Underscore.js declare a global variable “_”; these global variable names are assinged to “exports” prooperty. The “Exports” value must be the same as the global variable name. This is how RequireJS converts non-AMD components to be AMD compatible modules.
If you want to know more about “exports”, here’s a StackOverflow question and answer: RequireJS – What is the purpose of the “exports” property in shim (see comments on the answer that is checked)
The modules loaded by using the “shim” option are not done in parallel. One reason we are using RequireJS is to load are modules in asynchronously. If Underscore.js and Backbone.js were AMD compatible these modules would have loaded asynchronously. We will see how to do this in the next example.
I would like to go into more detail of using a “shim”, but I believe there is a better way of loading non compatible AMD modules. I wanted to show you a different option. In the next section we will learn about AMD compatible versions of Backbone.js and Underscore.js
***************************************************************************
Compatible AMD Backbone.js and Underscore.js
At one time Backbone.js and Underscore.js by default was AMD compatible. Jeremy Ashkenas the author of Backbone.js and Underscore.js believed that requiring code in the library to support a specific script loader such as AMD was not correct and remove the default support for AMD. We can make a few changes to Backbone.js and Underscore.js code to make them AMD compatible; it’s not difficult. But why make these changes when someone else has already done the work. James Burke the author of RequireJS has made a branch for Backbone.js and Underscore.js that are AMD compatible. These AMD compatible version can be found here: https://github.com/amdjs.
I downloaded the compatible AMD backbone.js and underscore.js files and renamed them to include AMD in the name. Now they are named backbone-amd.js and underscore-amd.js. If you look at the source of these files and do a search on “AMD”, you will see the changes occurred to support AMD.
Here are the major changes in Backbone.js to support AMD:
Here are the changes in Underscore.js to support AMD:
There are a few more changes in Backbone.js than what is shown to support AMD, but not many.
For Underscore.js, I believe what is show are all the changes needed to support AMD.
**************************************************************************
Using compatible AMD Backbone.js and Underscore.js with Require.js
As mentioned in the previous section, download the compatible AMD version of Backbone.js and Underscore.js from https://github.com/amdjs. After you download these files, rename them appropriately to Backbone-amd.js and Underscore-amd.js.
requirejs.config({ enforceDefine: true, paths: { "jquery": "libs/jquery-1.8.3", "underscore": "libs/underscore-amd", "backbone": "libs/backbone-amd" } }); define(["jquery", "underscore", "backbone"], function ($, _, Backbone) { console.log("Test output"); console.log("$: " + typeof $); console.log("_: " + typeof _); console.log("Backbone: " + typeof Backbone); } );
After you download the compatible AMD versions of Backbone.js and Underscore.js, change the file names in the main.js. That’s it.
The name for the backbone module can be anything, but the name for underscore must be “underscore”. For underscore, the capitalization is important.
In the output you should noticed that now the files download in parallel. Also we can confirm in the console that the modules loaded correctly.
**************************************************************************
Additional config options
There are a few additional options that I believe may be beneficial.
requirejs.config({ urlArgs: "bust=" + (new Date()).getTime(), baseUrl: "scripts", waitSeconds: 200, enforceDefine: true, paths: { "jquery": "libs/jquery-1.8.3", "underscore": "libs/underscore-amd", "backbone": "libs/backbone-amd" } }); define(["jquery", "underscore", "backbone"], function ($, _, Backbone) { console.log("Test output"); console.log("$: " + typeof $); console.log("_: " + typeof _); console.log("Backbone: " + typeof Backbone); } );
If you make a change to your javascript module and refresh your browser, the browser may retrieve the file from cache. To force the browser to retrieve the file from the server, use the config option “urlArgs”. The code “urlArgs: "bust=" + (new Date()).getTime(),
” integer to the end of the URL that represents a date (see image below). This only works for the modules; it does not work for the html file or main.js file.
urlArgs: Extra query string arguments appended to URLs that RequireJS uses to fetch resources. Most useful to cache bust when the browser or server is not configured correctly. Example cache bust setting for urlArgs: RequireJS urlArgs
urlArgs: "bust=" + (new Date()).getTime()
The baseUrl option is used to identify where the location of the scripts are located relative to the current file (main.js). For example, jQuery is located relative from main.js file at “scripts/libs/jquery-1.8.3.js”
baseUrl: the root path to use for all module lookups. So in the above example, “my/module”‘s script tag will have a src=”/another/path/my/module.js”. baseUrl is not used when loading plain .js files, those strings are used as-is, so a.js and b.js will be loaded from the same directory as the HTML page that contains the above snippet. RequireJS baseUrl
The waitSeconds option can be used when files may timeout when loading.
waitSeconds: The number of seconds to wait before giving up on loading a script. Setting it to 0 disables the timeout. The default is 7 seconds. RequireJS waitSeconds
I hope this help you get started with Require.js and Backbone.js.
Other good resources:
Hi,
Very very very good article ! It helps me a lot !
Could you add a project boilerplate for us ?
This is a very thorough and helpful article. Why do you think the AMD approach is better compared to using the shim config?
There are probably a couple of reasons to use the AMD compatible modules. At the top of my list is that the modules (Backbone, Underscore) can load in parallel. In the example above when a shim was used, Backbone.js and Underscore.js loaded synchronously. This is not a big problem, but imagine if there were 10 or more files that load synchronously.
Another reason to use a script loader such as Require.js is to remove the need to worry about dependencies. If we use the non-compliant AMD version of Backbone and Underscore then dependencies have to be identified in the shim.
As a personal preference the shim seems a little ugly.
There is at least one reason to use the standard version of Backbone.js and Underscore.js. It’s nice not having to modify or download additional files needed to support AMD.
James Burke (author of Backbone.js) mentioned that to support AMD scripts should not be required to be modified. I agree with this. What happens when a new type of script loader is created in the future? Should Backbone.js change to support the new script loader too?
The project I’m working on standardizes on RequireJS, even though it’s doesn’t feel completely correct to use a modified version of Backbone.js, I believe the pros outweigh the cons. But this is my preference. Who knows, a few months down the road I may change my mind and use shims or a different script loader.
James Burke is the author of Require.js, Jeremy Ashkenas is the author of Backbone.js.
We prefer using shims for all non AMD libraries and we have around 30 of such libs in a single project. That doesn’t mean each one of those is loaded one after another syncrhonously, only the ones that have dependencies have to load ins sequential order. So Require.js might be loading jquery and d3 and moment.js in parallel in the first step and then load 10 jquery plugins in parallel in the second step. This sync behaviour only applies to the few non AMD files that we’re using and is not a big problem, because for production we build all of the libs into a single file anyways. And using shim is very convenient, because you don’t need to modify the 3rd party code in any way.
One thing I would recommend doing is setting the requirejs baseUrl to “scripts/libs”, that is:
“`
requirejs.config({
baseUrl: “scripts/libs”,
paths: {
“app”: “../app”
}
});
“`
This way, you don’t need to configure the path for each 3rd party script, you can just do require(“underscore”) and it will get it from the libs dir. To require your app modules you then do require(“app/views/foo”).
You can find more good docs on structuring your project on volo wiki (https://github.com/volojs/volo/wiki)
Volo also provides a good boilerplate (https://github.com/volojs/volo). All you need to create a boilerplate project is the following.
“`
npm install -g volo
volo create myapp
// easily build the project (concatenate all files for production)
cd myapp
volo build
//add 3rd party libs
volo add jquery
“`
where can I download the source code?
thank you
I just added it to github at https://github.com/BarDev/RequireJs-BackboneShimAmd
Good tutorial. Keep posting…
very nice article……….
This is the best RequireJS tutorial I’ve found yet. I’ll help spread the word about this.
Its saved me many hours, thanks a ton!
Pingback: requireJS: shim or amd version of backbone, underscoreQueryBy | QueryBy, ejjuit, query, query by, queryby.com, android doubt, ios question, sql query, sqlite query, nodejsquery, dns query, update query, insert query, kony, mobilesecurity, postquery, query
Pingback: requireJS: shim or amd version of backbone, underscore | Technology & Programming Answers
Great tutorial!! An extremely easy explaination
Hi BarDev,
You rock, I never found such a down-to-earth explanation.
Tons of THANKS. Keep up your good work.
Hi Bar Dev,
Your article is really very very good. It explained all the concepts in a very easy and simple way. Till now i had been using shim but struggling to understand the finer points behind it. Hope to have more such wonderful articles in future from you
+10
Thank you so much! I just wanted to start debugging just the way you did, you saved me a lot of time!
Best article ever!!!