Distinct pieces of loosely coupled functionality implemented in a gardened environment (a module).
They facilitate easier maintainability of applications.
Not native to JavaScript (yet), not exactly new either...
var Module = (function() {
var private = "something";
return function() {
return {
someApi: { ... }
}
};
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Require.js Application</title>
<script src="vendor/require.js"></script>
</head>
<body>
</body>
</html>
// naming you module is optional, and in fact, you should avoid it
// naming your modules makes your code less portable
// dependencies listed in an array, same rules as require()
// mapped to arguments in the callback
define('name', ['dependency', 'bar'], function(dependency, bar) {
// Export the interface for our module.
return {
win: dependency.win,
lose: bar.lose
};
});
define(['dependency', 'bar'], function(dependency, bar) {
// Export the interface for our module.
return {
win: dependency.win,
lose: bar.lose
};
});
define(function() {
// Export the interface for our module.
return function(){
//functions can exported to!
};
});
define(function() {
var html = '<h1>Hello World!</h1>';
return {
start: function() {
document.body.innerHTML = html;
}
}
});
// foo is an external module
// foo can be a path or an alias to a path using require configuration
// the exports or return of foo will be mapped
// to the corresponding argument.
require(['foo'], function(foo) {
foo.win()
});
// Multiple dependencies
require(['foo', 'bar'], function(foo, bar) {
foo.win();
bar.lose();
});
// Dynamic loading of dependencies
define(['require', 'jquery'], function(require, $) {
// lots of awesome code
$('#chat-start').on('click', function(){
// dependencies are paths so this will grab features/chat.js
require(['features/chat'], function(chat) {
chat.start();
});
});
return //awesome api;
});
// multiple loaders!
var reqOne = require.config({
context: "version1",
baseUrl: "version1"
});
require(['app/main'], function(app) {
app.start();
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Require.js Application</title>
<script src="vendor/require.js" data-main="main"></script>
</head>
<body>
</body>
</html>
require.config({
baseUrl: '/js',
paths: {
// ;-)
'underscore': 'lodash'
}
});
require.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'
}
}
});
requirejs.config({
map: {
// When some/newmodule requires foo it gets the newer version.
'some/newmodule': {
'foo': 'foo1.2'
},
// When some/oldmodule requires foo it gets the older version.
'some/oldmodule': {
'foo': 'foo1.0'
}
}
});
requirejs.config({
paths: {
'handlebars' : 'vendor/handlebars-1.0.rc.1'
},
shim: {
'handlebars' : {
exports: 'Handlebars'
}
}
});
require(['app/main'], function(app) {
app.start();
});
requirejs.config({
paths: {
jquery: 'http://code.jquery.com/jquery-1.8.2.js'
}
});
require(['jquery'], function ($) {
//Do something with $ here
}, function (err) {
//The error has a list of modules that failed
var failedId = err.requireModules && err.requireModules[0];
if (failedId === 'jquery') {
//undef is function only on the global requirejs object.
//Use it to clear internal knowledge of jQuery. Any modules
//that were dependent on jQuery and in the middle of loading
//will not be loaded yet, they will wait until a valid jQuery
//does load.
requirejs.undef(failedId);
//Set the path to jQuery to local path
requirejs.config({
paths: {
jquery: 'local/jquery'
}
});
//Try again. Note that the above require callback
//with the "Do something with $ here" comment will
//be called if this new attempt to load jQuery succeeds.
require(['jquery'], function () {});
} else {
//Some other error. Maybe show message to the user.
}
});
requirejs.config({
// To get timely, correct error triggers in IE, force a define/shim exports check.
enforceDefine: true,
paths: {
jquery: [
'http://code.jquery.com/jquery-1.8.2.js',
//If the CDN location fails, load from this location
'lib/jquery'
]
}
});
//Later
require(['jquery'], function ($) {
});
require(['chat'], function(Chat){
Chat.start();
}, function(err){
console.log('Chat broke my heart and failed me.');
var failedId = err.requireModules && err.requireModules[0];
requirejs.undef(failedId);
// Log an error, call a service telling your developers
// to stop being n00bs.
require(['log'], function(log){
log.ourDevsAreScrubs();
});
// Maybe apologize to user through ui?
// Finally I hope this is merciful... round 2
console.log('Ok time to try it again.');
require(['chat'], function(Chat){});
});
requirejs.config({
paths: {
'handlebars' : 'vendor/handlebars-1.0.rc.1'
},
shim: {
'handlebars' : {
exports: 'Handlebars'
}
}
});
require(['app/main'], function(app) {
app.start();
}, function(err) {
console.log('Hey guys, we suck... :-( Time to restart!', err);
// Restart
require(['app/main'], function(app){
app.start();
});
});
define(function() {
var run = function(odds) {
var rand = Math.random();
if (rand > 1 - odds) {
throw new Error('Chaos Monkey Strikes!');
}
console.log('Consider yourself lucky.');
}
return run;
});
define(['handlebars', 'app/lib/chaos'], function(Handlebars, chaos) {
var template = Handlebars.compile('<h1>Hello {{name}}!</h1>');
return {
start: function() {
chaos(0.2);
document.body.innerHTML = template({ name: 'CascadiaJS' });
}
}
});
// the <plugin>!<resource> is the syntax to use a plugin
// plugins are just modules that implement a specific api
define(['foo!bar'], function(bar) {
// Export the interface for our module.
});
define(['cs!module.coffee'], function(module) {
// module was compiled for me!
});
define([
'text!mytemplate.handlebars',
'handlebars'
], function(template, handlebars){
// template is just a string
return handlebars.compile(template);
});
requirejs.config({
paths: {
'handlebars' : 'vendor/handlebars-1.0.rc.1',
'text' : 'vendor/text'
},
shim: {
'handlebars' : {
exports: 'Handlebars'
}
}
});
<h1>Hello {{name}}!</h1>
define([
'handlebars',
'app/lib/chaos',
'text!./templates/app.handlebars'
], function(Handlebars, chaos, text) {
var template = Handlebars.compile(text);
return {
start: function() {
chaos(0.2);
document.body.innerHTML = template({ name: 'CascadiaJS' });
}
}
});
That honor a specified API.
write: function (pluginName, name, write) { ... },
load: function (name, parentRequire, load, config) {
// Require the intended dependency
parentRequire([name], function (val) {
// Add extra functionality
val.extra = function () { alert('extra!'); };
// resolve the dependency manually
load(val);
});
}
require(['plugin!module'], function (module) {
module.extra(); // alerts 'extra!'
});
Plugins can implement a custom load strategy!
require([
'view!NavigationView',
'model/NavigationItems'
], function (NavigationView, NavigationItems) {
// NavigationView is device specific,
// imagine swapping out views for devices!
// Different for Android, iPhone, Browser or Whatever
new NavigationView({
model: new NavigationItems
});
});
There are lots of things your app doesn't need to do at runtime, do it at build time!
load: function (name, parentRequire, load, config) {
load.asText('hbs!'+name,
'define(["handlebars"], function (H){ '+
'return H.template(H.precompile(' + tmplTxt + '));' +
'})');
parentRequire(['hbs!'+name], function (val) {
load(val);
});
}
Uglify will remove dead code blocks like this, also has closure compiler support.
if (has('something') ) {
useSomething();
}
// changes to
if (false) {
doSomething();
}
{
"mainConfigFile": "main.js",
"name": "app/main",
"out": "app/main-built.js"
}
r.js -o build.js