Re-Learning Backbone.js – Require.js (AMD and Shim)

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:

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.

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.

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.

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.

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

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.

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”).

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