Skip to content

Instantly share code, notes, and snippets.

@jevakallio
Last active November 4, 2022 17:54
Show Gist options
  • Save jevakallio/4475666 to your computer and use it in GitHub Desktop.
Save jevakallio/4475666 to your computer and use it in GitHub Desktop.
A sample showcasing some of the things RequireJS can and can not do.
//let's say you have these three AMD modules:
//testmodule1.js
define(function() {
var val = { iAm:'testmodule1.js' };
console.log(val);
return val;
});
//testmodule2.js
define(function() {
var val = { iAm:'testmodule2.js' };
console.log(val);
return val;
});
//testmodule3.js
define(function() {
var val = { iAm:'testmodule3.js' };
console.log(val);
return val;
});
//#1 let's start with a standard AMD type module definition which requires above test modules
define(['testmodule1', 'testmodule2', 'testmodule3'], function(testmodule1, testmodule2, testmodule3) {
return {};
});
/* As expected, console says
testmodule1 Object {iAm: "testmodule1.js"} testmodule1.js:4
testmodule2 Object {iAm: "testmodule2.js"} testmodule2.js:4
testmodule3 Object {iAm: "testmodule3.js"} testmodule3.js:4
*/
//#2 Let's throw some synchronous nested "require" calls in there:
define(['testmodule1', 'testmodule2', 'testmodule3'], function(testmodule1, testmodule2, testmodule3) {
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
/* Looks like the synchronous require calls worked!
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
Object {iAm: "testmodule1.js"} Object {iAm: "testmodule2.js"} Object {iAm: "testmodule3.js"} requiretest.js:3
*/
//#3 But what if we don't import the modules in the define header?
define([], function() {
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
/* Console tells us:
Uncaught Error: Module name "testmodule1" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded require.js:160
*/
//So we can only use the "synchronous" require calls for modules which have already been loaded.
//#4 What if we use the CommonJS sugar provided by requirejs?
define(function(require, module, exports) {
console.log(
require('testmodule1'),
require('testmodule2'),
require('testmodule3'));
return {};
});
/* Looks like the synchronous require calls worked again!
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
Object {iAm: "testmodule1.js"} Object {iAm: "testmodule2.js"} Object {iAm: "testmodule3.js"} requiretest.js:3
*/
//#5 Let's mix it up, and load one module conditinally (if(false)... so never) and one module
// only when we need it...
define(function(require) {
console.log('testmodule1', require('testmodule1'));
if(false) {
console.log('testmodule2', require('testmodule2'));
}
function Foo() {
this.bar = function() {
var testmodule3 = require('testmodule3');
console.log('testmodule3', testmodule3);
};
}
return {};
});
/* And surprisingly, all the modules were loaded, even though only one "require" call was executed...
Object {iAm: "testmodule1.js"} testmodule1.js:4
Object {iAm: "testmodule2.js"} testmodule2.js:4
Object {iAm: "testmodule3.js"} testmodule3.js:4
testmodule1 Object {iAm: "testmodule1.js"} requiretest.js:3
*/
//This behavior is because when using the CommonJS syntax, requirejs parses the code file
//for nested require calls and loads all the found modules. For that reason you can't use
//variable names or non-literal string constructs in require calls.
//#6 For example:
define(function(require) {
var moduleName = 'testmodule1';
console.log('testmodule1', require(moduleName));
});
/* Will not work
Uncaught Error: Module name "testmodule1" has not been loaded yet for context: _.
http://requirejs.org/docs/errors.html#notloaded require.js:160
*/
//We can see the module resolution in action when we build the example #5 with r.js (with optimize:none).
//#7 We get:
define('requiretest',['require','testmodule1','testmodule2','testmodule3'],function(require) {
console.log('testmodule1', require('testmodule1'));
if(false) {
console.log('testmodule2', require('testmodule2'));
}
function Foo() {
this.bar = function() {
var testmodule3 = require('testmodule3');
console.log('testmodule3', testmodule3);
};
}
return {};
});
//RequireJS pulled all the nested require calls and lifted them to the top of the module.
//The compiled code works for the same reason #2 works - because RequireJS has already loaded
//and cached the modules, and can simply return cache[moduleName].
//#8 It's possible to lazy load modules using the require(['modulename'], function(module) {}); syntax.
define(function(require) {
console.log('testmodule1', require('testmodule1'));
if(false) {
require(['testmodule2'], function(testmodule2) {
console.log('loaded', testmodule2);
});
}
if(true) {
require(['testmodule3'], function(testmodule3) {
console.log('loaded', testmodule3);
});
}
return {};
});
/* Only the evaluated dependencies are loaded, and in correct order
Object {iAm: "testmodule1.js"} testmodule1.js:4
testmodule1 Object {iAm: "testmodule1.js"} requiretest.js:3
Object {iAm: "testmodule3.js"} testmodule3.js:4
loaded Object {iAm: "testmodule3.js"} requiretest.js:13
*/
//#9 As long as you use a string literal in the module definition, the build will work too. Here's output,
// where only the first module is evaluated at the top:
define('requiretest',['require','testmodule1'],function(require) {
console.log('testmodule1', require('testmodule1'));
if(false) {
require(['testmodule2'], function(testmodule2) {
console.log('loaded', testmodule2);
});
}
if(true) {
require(['testmodule3'], function(testmodule3) {
console.log('loaded', testmodule3);
});
}
return {};
});
/* But so in short:
1. The 'require' method is technically speaking synchronous, but can't be safely used,
because the modules may not be loaded in the context.
2. With the CommonJS sugar syntax you can call `require` safely, but you should know that
the modules are still loaded up front, unconditionally. Loading modules lazily with
var foo = require(bar) syntax is not possible.
3. RequireJS is magic.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment