Web Dev

RequireJS shim example

1. RequireJS

RequireJS is an AMD JavaScript library that supports asynchronous file and module loading. It is optimized for web browser usage but it is also possible to use it in Java or Node environments. It is compatible with all main browsers and quite intuitive to use. Its main benefit is that it offers the option to logically structure an application following the AMD principles.

In the article https://www.webcodegeeks.com/javascript/requirejs/requirejs-tutorial-how-to-use-requirejs/ you can find more information about how to use RequireJS.

2. Shim

Shim is a mechanism that RequireJS provides in order to support libraries and scripts that do not express their dependencies via define(). In other words, shim is there for traditional scripts not supporting definition of AMD modules. Several known libraries do not support AMD modules, so shim configuration is needed when loading them using RequireJS. Normally these libraries and scripts use the global space. Shim takes care of the needed dependencies, export and initialization of the non AMD scripts.

The shim mechanism can also be used to support the usage of different versions of the same library (non AMD) without corrupting the global namespace.

It is important to mention that shim cannot be used for loading and configuring AMD scripts, that is, you cannot configure AMD modules and scripts in the Shim configuration property and this is not going to work.

In the following example we are going to show how to configure your RequireJS application via the shim mechanism so traditional libraries not supporting AMD modules like Backbone or Underscore can be used.

3. Example non AMD

We are going to show a very simple application that uses RequireJS to handle its dependencies. First we load RequireJS with its main.js configuration file:

indexNoAlmond.html

//check download section to see HTML code

Here is our “standard” main.js file without shim configuration:

main.js

//check download section to see HTML code
requirejs.config({
    baseUrl: 'js',
    waitSeconds: 20,
    paths: {
        "jquery": "jquery",
        "backbone": "backbone",
        "underscore": "underscore"
    },
    deps: ['fx/App'],

    urlArgs: "t=20160320000000" //flusing cache, do not use in production
});

As you can see in the example we are trying to use Backbone and Underscore in our application. This is what happens when we launch our code:

require.js:1952 GET http://localhost:63342/requireJsGruntExample/web/js/underscore.1.8.3.js?t=20160320000000 req.load @ require.js:1952context.load @ require.js:1679Module.load @ require.js:829Module.fetch @ require.js:819Module.check @ require.js:851Module.enable @ require.js:1177context.enable @ require.js:1550(anonymous function) @ require.js:1162(anonymous function) @ require.js:131each @ require.js:56Module.enable @ require.js:1114Module.init @ require.js:783callGetModule @ require.js:1204context.completeLoad @ require.js:1583context.onScriptLoad @ require.js:1711
require.js:165 Uncaught Error: Script error for "underscore.1.8.3", needed by: backbone
http://requirejs.org/docs/errors.html#scripterror

This is an error while loading Backbone explaining that Backbone depends on Underscore. So we are going to modify our main.js configuration file to express this dependency:

main.js

//check download section to see HTML code
requirejs.config({
    baseUrl: 'js',
    waitSeconds: 20,
    paths: {
        "jquery": "jquery",
        "backbone": "backbone",
        "underscore": "underscore"
    },
    shim: {
        "backbone": {
            "deps": ["underscore", "jquery"],
            "exports": "Backbone"  //attaches "Backbone" to the window object
        }
    },
    deps: ['fx/App'],

    urlArgs: "t=20160320000000" //flusing cache, do not use in production
});

Now when loading our code we get the following exception:

DataConnector.js?t=20160320000000:17 Uncaught TypeError: Cannot read property 'isEqual' of undefined

This happens in the line:

DataConnector.js

//check download section to see HTML code
 if (_.isEqual("EUR", from)) {

And means that the object or module _ is not known or undefined, this can be solved by configuring the exports that the Underscore library does, we do this in our main.js configuration file as well:

main.js

//check download section to see HTML code
requirejs.config({
    baseUrl: 'js',
    waitSeconds: 20,
    paths: {
        "jquery": "jquery",
        "backbone": "backbone",
        "underscore": "underscore"
    },
    //for traditional libraries not using define() we need to use a shim that allows us to declare them as AMD modules
    shim: {
        "backbone": {
            "deps": ["underscore", "jquery"],
            "exports": "Backbone"  //attaches "Backbone" to the window object
        },
        "underscore": {
            exports: "_" // exporting _
        }
    },
    deps: ['fx/App'],

    urlArgs: "t=20160320000000" //flusing cache, do not use in production
});

Now everything is working fine.

It is also fair to mention that these only happens with old versions of Backbone and Underscore. The last versions of these libraries work pretty well with AMD.

From http://underscorejs.org/underscore.js:

// AMD registration happens at the end for compatibility with AMD loaders
  // that may not enforce next-turn semantics on modules. Even though general
  // practice for AMD registration is to be anonymous, underscore registers
  // as a named module because, like jQuery, it is a base library that is
  // popular enough to be bundled in a third party lib, but not be part of
  // an AMD load request. Those cases could generate an error when an
  // anonymous define() is called outside of a loader request.
  if (typeof define === 'function' && define.amd) {
    define('underscore', [], function() {
      return _;
    });
  }

From http://backbonejs.org/backbone.js:

// Set up Backbone appropriately for the environment. Start with AMD.
  if (typeof define === 'function' && define.amd) {
    define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
      // Export global even in AMD case in case this script is loaded with
      // others that may still expect a global Backbone.
      root.Backbone = factory(root, exports, _, $);
    });

4. Download

Download
In the following link you can download a full working example covering the main components explained in this article: requireJsShimExample

5. Links

You can find more in deep information about RequireJS and shim configuration in the following links:

Dani Buiza

Daniel Gutierrez Diez holds a Master in Computer Science Engineering from the University of Oviedo (Spain) and a Post Grade as Specialist in Foreign Trade from the UNED (Spain). Daniel has been working for different clients and companies in several Java projects as programmer, designer, trainer, consultant and technical lead.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button