What is the purpose of the "exports" property in the shim below? Is it really required?
requirejs.config({
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
I ask because it seems redundant - when the module is included in a dependency list, we will specify the exported name again as the function argument:
define(['backbone'], function (Backbone) {
return Backbone.Model.extend({});
});
If shim is not used in your example then the Backbone object you pass in as a parameter would be undefined as Backbone is not AMD compliant and does not return an object for RequireJS to use.
define(['backbone'], function (Backbone) {
// No shim? Then Backbone here is undefined as it may
// load out of order and you'll get an error when
// trying to use Model
return Backbone.Model.extend({});
});
To give a bit of context I will use the code that the r.js optimiser spits out but I will simplify it for this example. It helped me understand the point of it by reading what the optimiser produces.
The shimmed Backbone would be a little like this:
// Create self invoked function with the global 'this'
// passed in. Here it would be window
define("backbone", (function (global) {
// When user requires the 'backbone' module
// as a dependency, simply return them window.Backbone
// so that properites can be accessed
return function () {
return global.Backbone;
};
}(this)));
The point is to give RequireJS something to return back to you when you ask for a module, and it will ensure that is loaded first before doing so. In the case of the optimiser, it will simply embed the library before hand.
If you don't use "export" Backbone, then you can't get the locale reference in module to Backbone(window.Backbone) which is defined in backbone.js.
//without export Backbone
shim : {
'bbn':{
//exports:'Backbone',
deps:['underscore']
},
'underscore': {
exports: '_'
}
};
require(['bbn'], function(localBackbone) {
//localBackbone undefined.
console.log('localBackbone:,' localBackbone);
});
RequireJs explains as follow:
//RequireJS will use the shim config to properly load 'backbone' and give a local
//reference to this module. The global Backbone will still exist on
//the page too.
define(['backbone'], function (Backbone) {
return Backbone.Model.extend({});
});
RequireJS will use shim config to get global Backbone
function getGlobal(value) {
if (!value) {
return value;
}
var g = global;
each(value.split('.'), function (part) {
g = g[part];
});
return g;
}
Also note that you might want to use actual export of the plugin in "exports". For example,
requirejs.config({
shim: {
'jquery.colorize': {
deps: ['jquery'],
exports: 'jQuery.fn.colorize'
},
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
},
'backbone.layoutmanager': {
deps: ['backbone']
exports: 'Backbone.LayoutManager'
},
"jqueryui": {
deps: ["jquery"],
//This is because jQueryUI plugin exports many things, we would just
//have reference to main jQuery object. RequireJS will make sure to
//have loaded jqueryui script.
exports: "jQuery"
},
"jstree": {
deps: ["jquery", "jqueryui", "jquery.hotkeys", "jquery.cookie"],
exports: "jQuery.fn.jstree"
},
"jquery.hotkeys": {
deps: ["jquery"],
exports: "jQuery" //This plugins don't export object in jQuery.fn
},
"jquery.cookie": {
deps: ["jquery"],
exports: "jQuery" //This plugins don't export object in jQuery.fn
}
}
});
More: https://github.com/jrburke/requirejs/wiki/Upgrading-to-RequireJS-2.0#wiki-shim
Shim exports is for letting requirejs know how to handle non-AMD modules. Without it, dependencies in the define block will still be loading, while the module starts. It signals requirejs that it has stopped loading the resource and that modules can start using it.
At least, that's how i see it.
Related
On some occasions, requirejs returns an undefined object to my module. I've looked at a number of posts and most of the answer are related to circular dependencies. However I could find none (I have checked several times). I apologize by advance for pasting a quantity of code that I've tried to reduced to the minimum :) Any help would be greatly appreciated!
Here is the module init_app.js that fails:
define([
'marionette',
], function(
Marionette
) {
"use strict";
var App;
App = new Marionette.Application();
App.addRegions({ body: "#main_body" });
return App;
});
Sometimes the Marionette module is undefined. Here is the part of my config.js that might be relevant:
define([], function() {
'use strict';
require.config({
baseUrl: 'js',
paths : {
underscore : 'vendors/underscore/underscore',
jquery : 'vendors/jquery/dist/jquery',
backbone : 'vendors/backbone/backbone',
marionette : 'vendors/marionette/lib/backbone.marionette',
wreqr : 'vendors/backbone.wreqr/lib/backbone.wreqr',
eventbinder : 'vendors/backbone.eventbinder/lib/backbone.eventbinder',
babysitter : 'vendors/backbone.babysitter/lib/backbone.babysitter',
},
shim : {
jquery : {
exports : 'jQuery'
},
underscore : {
exports : '_'
},
backbone : {
deps : ['jquery', 'underscore'],
exports : 'Backbone'
},
wreqr: {
deps : ['backbone'],
exports: 'Backbone.Wreqr'
},
eventbinder : {
deps : ['backbone']
},
babysitter : {
deps: ['backbone']
},
marionette : {
deps: ['backbone', 'wreqr', 'eventbinder', 'babysitter'],
exports : 'Marionette'
},
}
});
});
The main.js file is
require(['config'], function() {
require( ['app'], function (App) {
App.start({});
});
});
where the app.js file is
define([
'init_app',
'router',
], function(
App,
Router
) {
"use strict";
App.on('start', function() {
new Router();
Backbone.history.start();
});
return App;
});
And the router will define a bunch of things that might depend on init_app.js. I've been especially carefull that none of them defines the app.js, which should be sufficient to guaranty that no circular dependency could cause this bug. Any clue??
You should review your shim configuration to remove all those shims that you've put in for modules that actually use define. For instance, jQuery uses define and thus does not need a shim. The same is true of Marionette. I've just installed it with Bower and found this towards the start of the file:
if (typeof define === 'function' && define.amd) {
define(['backbone', 'underscore'], function(Backbone, _) {
return (root.Marionette = root.Mn = factory(root, Backbone, _));
});
}
...
If you see something like this in a module you use, or a flat out call to define, then you should not use a shim for it.
I've not checked every single module you use. Please review all of them to make sure you do not use shims where not needed. If you use shims incorrectly, you can get undefined values for your modules.
Here is how I solved it: I changed the main.js to
require(['config'], function() {
require( ['init_app'], function () {
require( ['app'], function () {
App.start({});
});
});
});
and put the App in the global scope in init_app. This works well but doesn't explain the previous failure.
I am loading a 3rd party script that simply creates an overlay on a site it has been loaded onto. It works fine but sites using require.js seem to have intermittent issues I'm assuming with async loading some js files. Is there any type of callback or way to create a module in the DOM as sort of a listener to see if require.js is done loading?
I tried this but not even close:
define(function() {
alert('test');
return {};
});
and
define('myModule',
function () {
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
};
return myModule;
});
console.log(myModule);
I ended up just creating a secondary require.config file and loading the module with require if require is detected, seems to work fine.
if(typeof require === 'function') {
var base = 'http://' + someDomainVar;
function getJSTreeURL() {
var url = base + '/js/libs/jstree.min';
return url;
}
function getModuleURL() {
var url = base + '/module';
return url;
}
var reqTwo = require.config({
context: "instance2",
baseUrl: "instance2",
paths: {
'jq': 'http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min',
'jqTree': getJSTreeURL(),
'module': getModuleURL()
},
shim: {
'jq': {
exports: 'jq'
},
'jqTree': {
deps: ['jq'],
exports: 'jqTree'
},
'module': {
deps: ['jq', 'jqTree'],
exports: 'module'
}
}
});
reqTwo(['require', 'jq', 'jqTree'],
function(require, jq, jqTree) {
setTimeout(function() {
require(['module'],
function(module) {
console.log('loaded');
}
);
}, 0);
});
I'm trying to migrate a site to use RequireJS to manage it's JS dependencies. Also I want to bundle some libs together.
Currently we are building a base.min.js that comprises underscore, jquery, bootstrap and backbone. They are used all over our site and thus it makes sense to serve them together.
Nevertheless, I think we should have logically the three libs separate by name, thus I have written the following require.config:
require.config({
baseUrl: '/s/js/libs',
paths: {
app: '../app', shims: '../shims'
},
map: {
'*' : {
// underscore, backbone, jquery and bootstrap are bundled
'underscore': '../base',
'backbone': '../base',
'jquery': '../base',
'bootstrap': '../base'
}
},
shim:{
'bootstrap': {
deps: ['jquery']
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
},
'jquery': {exports: '$'}
}
});
I'm not using the data-main; but instead I'm requiring several things:
require(["jquery", "underscore", "backbone", "bootstrap", "../baseapp"],
function($){
// Next line fixes the bootstrap issue with double modals taken from:
// http://stackoverflow.com/questions/13649459/twitter-bootstrap-multiple-modal-error
$.fn.modal.Constructor.prototype.enforceFocus = function () {};
$('.modal').on('shown', function () {
$('input:text:visible:first, textarea:visible:first', this).focus();
});
$('#search').on('shown', function () {
$('#id_asf-text').focus();
})
require(['micro', 'csrf_xhr', 'locale']);
require(['app/routers']);
});
This however causes the error that $ is undefined.
The global window.$ is defined, but it seems that requirejs is not properly detecting it with the exports of my shims. Even if I do exports: 'window.jQuery' it does not work.
Is this a bug in RequireJS or there a bug in my code? Does map and shim play well together? Does RequireJS support my use case?
Update 2013-11-05
After a long debugging session, I have found that shims are recorded per "real" Module inside RequireJS; so if I just change my shim to be:
shim : {
'../base': {init: function() {return [$, _, Backbone]}}
}
I do get this array as the first argument to my callback. However I would like them to be exploded, i.e; have each of the returned values as arguments...
I thought that an internal map + paths would work. Like this:
var require = {
baseUrl: '/s/js/libs/',
paths: {
app: '../app',
shims: '../shims',
'base-underscore': '../base',
'base-backbone': '../base',
'base-jquery': '../base',
'base-bootstrap': '../base'
},
map: {
'*' : {
// underscore, backbone, jquery and bootstrap are bundled
'underscore': 'base-underscore',
'backbone': 'base-backbone',
'jquery': 'base-jquery',
'bootstrap': 'base-bootstrap',
}
},
shim:{
'base-bootstrap': {
deps: ['base-jquery'],
init: function() {return null}
},
'base-backbone': {
deps: ['base-underscore', 'base-jquery'],
init: function() {return window.Backbone;}
},
'base-underscore': {
init: function() {return window.Underscore;}
},
'base-jquery': {
init: function() {return $}
}
} // shims
};
Unfortunally, it does not. Now the error is: Uncaught Error: Load timeout for modules: base-underscore,base-backbone,base-bootstrap,../base... Notice base-jquery is not listed!
I have found a workaround, but involves a plugin. This is my current solution. On my HTML I have the following config (I changed to require = {...} idiom so that debugging would be easier):
var STATIC_URL = "/s/js/libs/";
var require = {
baseUrl: STATIC_URL,
paths: {
app: '../app',
shims: '../shims',
},
map: {
'*': {
'jquery': 'bundler!jQuery',
'bootstrap': 'bundler!',
'underscore': 'bundler!_',
'backbone': 'bundler!Backbone'
}
},
bundler: {url: '../base'}
};
The bundler.js plugin lies in my js/libs. This is the original CoffeeScript:
global = #
each = (ary, func) ->
if (ary)
i = 0
while i < ary.length and (what = ary[i]) and func(what, i, ary)
i += 1
getGlobal = (value) ->
if not value
value
g = global;
each value.split('.'), (part) ->
g = g[part]
g
define
load: (name, require, onload, config) ->
base = config.bundler?.url ? '../base'
require [base], () ->
if name? and name
value = getGlobal(name)
onload(value)
else
onload()
normalize: (name, norm) -> name
Probably there should be a way to do this without a bundler... I'll keep the question open for while, so that better answers might be provided.
im struggling with a weird Issue with Backbone.Marionette an requireJS.
RquireJS is configured like https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs says:
require.config({
deps: ['main'],
paths : {
backbone : '../vendor/backbone.marionette/backbone',
underscore : '../vendor/underscore/underscore',
jquery : '../vendor/jquery/jquery',
marionette : '../vendor/backbone.marionette/backbone.marionette.min'
},
shim : {
jquery : {
exports : 'jQuery'
},
underscore : {
exports : '_'
},
backbone : {
deps : ['jquery', 'underscore'],
exports : 'Backbone'
},
marionette : {
deps : ['jquery', 'underscore', 'backbone'],
exports : 'Marionette'
}
}
});
The main.js:
require([
'app'
],
function(App) {
App.start();
}
);
And the app.js:
define([
'marionette'
],
function(Marionette) {
var app = Marionette.Application();
return app;
}
);
But when I wanna start a Application my Console says:
Uncaught TypeError: Object #<Object> has no method '_initRegionManager'
I did nothing special so far:
define(
[
'marionette'
],
function(Marionette) {
"use strict";
var app = Marionette.Application();
// app.on('initialize:after', function() {
// console.log("Initialize:After");
// });
return app;
}
);
And in the main.js (Startingpoint) i require the code above and wanna start it.
But it fails at Marionette.Application();
When i look into the marionette.js i can clearly see underscore extending the Application with the _initRegionManager-Method. Also in the Prototype-list of the Marionette-Object i can see the method.
Any ideas what i'm missing here?
Your require.config ({ … }) should be in main.js and also as Ratweb_on indicated, there should not be “deps: [‘main’]” in the require.config.
You can follow this example in here, and ignore the jquerymobile stuffs. Essentially it dose the initialization in the same way as your code intended.
See main.js and app.js.
Updated
In your app.js
var app = Marionette.Application();
Should be
var app = new Marionette.Application();
I've got the following requireJS config. When trying to reference the package/ImagingX module I always get undefined even though I can see that the script has been loaded in firebug. If I move the js file in question into the baseUrl directory and remove package/ it works as expected.
What am I doing wrong?
window.requirejs.config(
{
baseUrl: '/Scripts',
paths: {
"jquery": "./jquery-1.7.1.min",
"jqx": "/Content/Plugins/jqWidgets",
"package" : "/Scripts/packages"
},
urlArgs: "bust=" + (new Date()).getTime(),
shim : {
'jqx/jqxcore': ['jquery'],
'jqx/jqxsplitter': ['jquery','jqx/jqxcore']
}
}
);
window.require(['jquery', 'layoutManager', 'container', 'package/ImagingX'],
function ($,lm,container,px) {
px.Focus();
$(document).ready(function () {
lm.Init(); // Sets up panes
container.Init(); //Set up the containers
});
});
Update 15/10/2012
I'm getting desperate to solve this issue now, I've stripped everything back to the basics so here is the new main file :
(function () {
requirejs.config({
paths: {
"packages": "packages"
}
});
require([
'packages/testmodule'
],
function (tm) {
alert(tm);
});
})();
And the module which is in a sub folder called packages.
define('testmodule',
function () {
alert("called");
return {
set : 'rar '
};
});
I can see the script loaded but it never gets executed, hence I never get a reference for it.
requirejs.config({
paths: {
//"jquery": "./jquery-1.8.2.min",
//"jqx": "/Content/Plugins/jqWidgets",
"templates": 'templates',
"text": "commonRequireJsModules/text",
"domReady": "commonRequireJsModules/domReady",
"packages" : 'packages/'
//'signalR': './jquery.signalR-0.5.3.min',
//'knockout': './knockout-2.1.0',
//'pubsub' : './pubsub'
}
//,urlArgs: "bust=" + (new Date()).getTime()
//,
//shim : {
// 'jqx/jqxcore': ['jquery'],
// 'jqx/jqxsplitter': ['jquery', 'jqx/jqxcore'],
// 'signalR': ['jquery'],
// 'pubsub' : ['jquery']
//}
});
The trailing slash on the packages path seems to have addressed the issue, in part with also removing the name in the define part of the module. So it now looks like
define(['deps'],function(deps){
});
Rather than
define('myMod',['deps'],function(deps){
});
Couple of things:
it seems strange to use window.require instead of just require
names in 'shim' must match names in 'paths', this is not the case, here
document.ready is done for free by require, no need to do it again
So: do you have any loading error in your JS console? Does it says a script is missing?
Here is a working require configuration, Router is in the same folder of this code:
require.config({
paths:{
'jquery':'lib/jquery.min',
'backbone':'lib/backbone.min',
'underscore':'lib/underscore.min',
'router':'Router'
},
shim:{
'backbone':{ deps:['jquery', 'underscore'] },
'router':{ deps:['backbone'] }
}
});
require(['router', 'jquery', 'underscore', 'backbone'],
function (Router) {
var router = new Router();
$('img').hide();
});
});
And the index.html:
<html>
<head>
<script data-main="assets/js/App.js" src="assets/js/lib/require.min.js"></script>
</head>
<body>...</body>
</html>