ReqUire.js
Using AMD to fight for the good and against the bad and ugly parts of javascript
Mikael Karon <mikael.karon@ef.com>
EF Labs (Shanghai)
EF Labs (Shanghai)
Meet the family
The problem
We don't build web pages any more
We build web applications
The ugly:Manual deps
<script src="jquery.js"></script>
<script src="jquery.easing.js"></script>
<script src="slideshow.js"></script>
...
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-12345678-1']);
</script>
We know that JavaScript tags block page rendering
Not every JavaScript file is needed when the page first loads
Multi-page applications start copying & pasting script tags
As the site grows, so does complexity
No encapsulation of export of value = namespace pollution
The bad:Inlined closures
(function () {
var $ = this.jQuery;
var slideshow = $('#id').slideme();
this.myExample = function () {};
}());
The good:AMD
define(['widget'], function (Widget) {
return Widget.extend({
"method" : function () {
...
}
});
});
Modules are encapsulated and sand-boxed
Create and reuse code from different products
Structured, clean code base
Async loading, managed dependencies
Lazy load modules (stuff not used by the user yet can be loaded in background when needed on demand)
Not a technology, but a specification proposal
define() API
- Used to define a module
- Specifies all module dependencies
- Optionally name your module
- Export your module interface
define() API
// 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() API
define(['dependency', 'bar'], function(dependency, bar) {
// Export the interface for our module.
return {
win: dependency.win,
lose: bar.lose
};
});
dEFINE() API
define(function() {
// Export the interface for our module.
return function(){
//functions can exported to!
};
});
rEQUIRE() API
- Used to load code in a top level JavaScript file
- An entry point of sorts
- Used to lazily load code
rEQUIRE() API
// 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()
});
rEQUIRE() API
// Multiple dependencies
require(['foo', 'bar'], function(foo, bar) {
foo.win();
bar.lose();
});
rEQUIRE() API
// 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;
});
rEQUIRE() API
// multiple loaders!
var reqOne = require.config({
context: "version1",
baseUrl: "version1"
});
require.config() API
- Configuration of the Require.js Loader
- Plugin configuration
- Paths, packages, shims, map and more
require.config() API
require.config({
baseUrl: '/js',
paths: {
// ;-)
'underscore': 'lodash'
}
});
REQUIRE.CONFIG() API
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'
}
}
});
require.config() API
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'
}
}
});
Plugins:using
- Transparent middle ware for your modules!
- Load different kinds of assets. Assets are dependencies too! (CSS, Templates, etc)
- Can be used to pre-process module contents or load strategies. (CoffeeScript, Non-AMD Scripts)
- text!, css!, i18n!, cs!, json!, mdown!, jade! & lots more!
PLUGINS:USING
// 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.
});
PLUGINS:USING
define(['cs!module.coffee'], function(module) {
// module was compiled for me!
});
PLUGINS:USING
define([
'text!mytemplate.handlebars',
'handlebars'
], function(template, handlebars){
// template is just a string
return handlebars.compile(template);
});
PLUGINS:creating
- We can significantly reduce boilerplate, and further separate our concerns with plugins!
- The plugins will actually write to file in the build, this means your whole application could be a single request
PLUGINS:creating
Plugins are just modules
That honour 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!'
});
Questions?
Require.js
By Mikael Karon
Require.js
- 706