Requirejs why and when to use shim config
Asked Answered
R

3

101

I read the requirejs document from here API

requirejs.config({
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                return this.Foo.noConflict();
            }
        }
    }
});

but i am not getting shim part of it. why should i use shim and how should i configure, can i get some more clarification

please can any one explain with example why and when should we use shim. thanks.

Rampant answered 18/3, 2013 at 6:45 Comment(0)
V
113

A primary use of shim is with libraries that don't support AMD, but you need to manage their dependencies. For example, in the Backbone and Underscore example above: you know that Backbone requires Underscore, so suppose you wrote your code like this:

require(['underscore', 'backbone']
, function( Underscore, Backbone ) {

    // do something with Backbone

}

RequireJS will kick off asynchronous requests for both Underscore and Backbone, but you don't know which one will come back first so it's possible that Backbone would try to do something with Underscore before it's loaded.

NOTE: this underscore/backbone example was written before both those libraries supported AMD. But the principle holds true for any libraries today that don't support AMD.

The "init" hook lets you do other advanced things, e.g. if a library would normally export two different things into the global namespace but you want to redefine them under a single namespace. Or, maybe you want to do some monkey patching on a methods in the library that you're loading.

More background:

Votive answered 18/3, 2013 at 20:50 Comment(3)
Like yout example code, the Underscore and Backbone here use like the normal, what's shim do in this case? Can I use require( function() { _.extend({}); })? Does it understand _?Joby
"RequireJS will kick off asynchronous requests for both Underscore and Backbone" -> Is it possible to prevent this, in the case that the library is already loaded ?Octachord
@Octachord right, if the library is already loaded it won't kick off another server request, but the point of RequireJS is that your code doesn't need to care about if/how that happens. Perhaps start a new question for your particular use case?Votive
C
66

As per RequireJS API documentation, shim lets you

Configure the dependencies, exports, and custom initialization for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value.

- Configuring dependencies

Lets say you have 2 javascript modules(moduleA and moduleB) and one of them(moduleA) depends on the other(moduleB). Both of these are necessary for your own module so you specify the dependencies in require() or define()

require(['moduleA','moduleB'],function(A,B ) {
    ...
}

But since require itself follow AMD, you have no idea which one would be fetched early. This is where shim comes to rescue.

require.config({
    shim:{
       moduleA:{
         deps:['moduleB']
        } 
    }

})

This would make sure moduleB is always fetched before moduleA is loaded.

- Configuring exports

Shim export tells RequireJS what member on the global object (the window, assuming you're in a browser, of course) is the actual module value. Lets say moduleA adds itself to the window as 'modA'(just like jQuery and underscore does as $ and _ respectively), then we make our exports value 'modA'.

require.config({
    shim:{
       moduleA:{
         exports:'modA'
        } 
    }

It will give RequireJS a local reference to this module. The global modA will still exist on the page too.

-Custom initialization for older "browser global" scripts

This is probably the most important feature of shim config which allow us to add 'browser global', 'non-AMD' scripts(that do not follow modular pattern either) as dependencies in our own module.

Lets say moduleB is plain old javascript with just two functions funcA() and funcB().

function funcA(){
    console.log("this is function A")
}
function funcB(){
    console.log("this is function B")
}

Though both of these functions are available in window scope, RequireJS recommends us to use them through their global identifier/handle to avoid confusions. So configuring the shim as

shim: {
    moduleB: {
        deps: ["jquery"],
        exports: "funcB",
        init: function () {
            return {
                funcA: funcA,
                funcB: funcB
            };
        }
    }
}

The return value from init function is used as the module export value instead of the object found via the 'exports' string. This will allow us to use funcB in our own module as

require(["moduleA","moduleB"], function(A, B){
    B.funcB()
})

Hope this helped.

Costar answered 22/1, 2015 at 18:52 Comment(4)
Easy to understand! One question: in the last example, is the "exports" property simply ignored?Borg
No, its not being ignored. Had "exports" property been ignored in the last example, then the object you pass in as a parameter('B' in this case) would be undefined as moduleB is NOT AMD compliant and would not have returned an object for RequireJS to use(hence 'B.funcB' would not work).Costar
Hmm. I thought the value of the export would be overrode by the object that is returned in the init function. So the parameter B would be the object {funcA: funcA, funcB: funcB}, not simply funcB by itself. Is this not true?Borg
Niko Bellic is right, export IS ignored (I've just tested that). Object B is the object returned by the function specified in 'init' part. If you removed the 'init' part, object B would become the function funcB, so you would simply do B() instead of B.funcB(). And obviously funcA would become inaccessible in that case.Busywork
O
-2

You must add paths in requirejs.config to declare, example:

requirejs.config({
    paths: {
          'underscore' : '.../example/XX.js' // your JavaScript file
          'jquery' : '.../example/jquery.js' // your JavaScript file
    }
    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                return this.Foo.noConflict();
            }
        }
    }
});
Ovule answered 19/5, 2016 at 9:47 Comment(3)
This answer is a code dump that does nothing to explain "why and when to use shim config". If you edit your answer to provide an explanation, make sure you are adding something new, that is not already covered by earlier answersAarau
copy paste without any positive feedbackTermless
should be a comma before shim:Chanty

© 2022 - 2024 — McMap. All rights reserved.