Write Better Javascript with RequireJS What is RequireJS? What is RequireJS? from requirejs.org, "RequireJS is a JavaScript file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments, like Rhino and Node. Using a modular script loader like RequireJS will improve the speed and quality of your code." What is RequireJS? • • • • • Async Script Loader 12 k Very actively developed Well documented new BSD / MIT Why use a tool like RequireJS? Why use a tool like RequireJS? Because Javascript Sucks. Also, user side scripting makes things worse. JS sucks . ├── ├── ├── │ │ │ │ │ │ │ │ ├── │ │ │ │ │ │ │ │ │ │ │ │ ├── │ │ │ │ │ │ local_settings.py manage.py datacleaning ├── admin.py ├── forms.py ├── migrations │ └── 0001_initial.py ├── models.py ├── tests.py ├── urls.py └── views.py journals ├── admin.py ├── fixtures │ └── 20101124_5.03.json ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_auto__images__add_entry.py │ └── 0003_loading_test_journal_entries.py ├── models.py ├── tests.py ├── urls.py └── views.py lib ├── context_processors.py ├── string_processors.py ├── template.py ├── urlmiddleware.py ├── user.py └── widgets.py ├── │ │ │ │ │ │ │ │ │ │ │ │ ... parks ├── admin.py ├── fixtures │ ├── initial_featurecategories.json │ └── train_examples.json ├── forms.py ├── importers │ └── recdata.py ├── management │ ├── commands │ │ ├── exportoregonparks.py │ │ ├── importparks.py │ │ ├── ... JS sucks . ├── │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ckeditor ├── ckeditor.js ├── contents.css ├── lang │ ├── af.js │ ├── ar.js │ ├── bg.js │ ├── bn.js │ ├── bs.js │ ├── ca.js │ ├── cs.js │ ├── cy.js │ ├── da.js │ ├── de.js │ ├── el.js │ ├── en-au.js │ ├── en-ca.js │ ├── en-gb.js │ ├── en.js │ ├── eo.js │ ├── es.js │ ├── et.js │ ├── eu.js │ ├── fa.js │ ├── fi.js │ ├── fo.js │ ├── fr-ca.js │ ├── fr.js │ ├── gl.js │ ├── gu.js │ ├── he.js │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── │ │ ├── hi.js ├── hr.js ├── hu.js ├── is.js ├── it.js ├── ja.js ├── ka.js ├── km.js ├── ko.js ├── _languages.js ├── lt.js ├── lv.js ├── mn.js ├── ms.js ├── nb.js ├── nl.js ├── no.js ├── pl.js ├── pt-br.js ├── pt.js ├── ro.js ├── ru.js ├── sk.js ├── sl.js ├── sr.js ├── sr-latn.js ├── sv.js ├── th.js ├── _trans.txt ├── tr.js ├── uk.js ├── vi.js ├── zh-cn.js └── zh.js plugins └── styles └── styles │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── ├── ├── ├── │ │ ├── ├── │ │ │ ├── ├── ├── └── │ └── default.js ├── skins │ └── kama │ ├── dialog.css │ ├── editor.css │ ├── icons.png │ ├── icons_rtl.png │ ├── images │ │ ├── dialog_sides.gif │ │ ├── dialog_sides.png │ │ ├── dialog_sides_rtl.png │ │ ├── mini.gif │ │ ├── noimage.png │ │ ├── sprites_ie6.png │ │ ├── sprites.png │ │ └── toolbar_start.gif │ ├── skin.js │ └── templates.css └── SQRLY_TEAM_PLEASE_README__LICENSE elevationservice_eg_google.js jquery.form.js jquery.history.js jqueryplugins ├── jquery.address-1.3.js └── jquery.simplemodal-1.3.min.js markers.js pages ├── add-park.js ├── datacleaning.js └── park-detail.js park-ratings.js polylinearray_eg_google.js star-rating.js star-rating-metadata.js JS sucks 1. code is too long 2. just a little code from somewhere else 3. copy and paste 4. goto: 1 JS sucks <head> <script <script <script <script <script <script <script <script <script <script <script <script <script <script <script ... </head> src="foo.js" type="text/javascript"></script> src="bar.js" type="text/javascript"></script> src="baz.js" type="text/javascript"></script> src="biff.js" type="text/javascript"></script> src="bop.js" type="text/javascript"></script> src="foo.js" type="text/javascript"></script> src="bar.js" type="text/javascript"></script> src="baz.js" type="text/javascript"></script> src="biff.js" type="text/javascript"></script> src="bop.js" type="text/javascript"></script> src="foo.js" type="text/javascript"></script> src="bar.js" type="text/javascript"></script> src="baz.js" type="text/javascript"></script> src="biff.js" type="text/javascript"></script> src="bop.js" type="text/javascript"></script> RequireJS makes JS suck less • Help you make your code more modular • manages script loading for you • build and compress your code (oh, and other stuff too...) Making code more modular So what's the code look like? Loading from the page <script data-main="pages/profile.js" src="scripts/require.js"></script> <script src="scripts/require.js"></script> require(["pages/profile"]); Loading from the page require(["pages/profile"], function(Profile) { ...do stuff... require.ready() { ...do more stuff... Profile.init(INSERT_SOMETHING_HERE); }); }); Defining a Module define(["foo", "baz", "x"], function(Foo, Baz) { ...do stuff... return { init: function(data) { container = data; }, do_something: some_internal_method } }); Loading - production vs dev Loading - production vs dev Loading - production vs dev Let's look at an example require("map", function(Map) { ... map file is loaded ... }); require("map", function(Map) { ... map file is loaded ... }); require("map", function(Map) { ... map file is loaded ... }); define(["gm", "foo"], function(GM, Foo) { ... set up js ... return {}; }); require("map", function(Map) { ... map file is loaded ... }); define(["gm", "foo"], function(GM, Foo) { ... set up js ... return {}; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { ... page is ready ... }); }); define(["gm", "foo"], function(GM, Foo) { ... set up js ... return {}; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init(); }); }); define(["gm", "foo"], function(GM, Foo) { ... set up js ... return {}; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init(); }); }); define(["gm", "foo"], function(GM, Foo) { ... set up js ... return {}; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init(); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return {}; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init(); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init(); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init }; }); That's it. require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); a = Map.m; }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); a = Map.m; Map.init("another_div_id"); b = Map.m; }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { ... map file is loaded ... require.ready(function() { Map.init("div_id"); a = Map.m; Map.init("another_div_id"); b = Map.m; }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { Map.init("div_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { Map.init("div_id"); a = Map; }); }); require("map", function(Map) { require.ready(function() { Map.init("another_div_id"); b = Map; }); }); // a === b define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require({context:"abc"}, "map", function(Map) { require.ready(function() { Map.init("div_id"); a = Map.m; }); }); require({context:"xyz"}, "map", function(Map) { require.ready(function() { Map.init("another_div_id"); b = Map.m; }); }); // a.m !== b.m define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { var m; function init(id) { m = GM.create(id); } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { function init(id) { var that = {}; that.m = GM.create(id); return that; } return { init: init, map: m }; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { function init(id) { var that = {}; that.m = GM.create(id); return that; } return { init: init }; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { function init(id) { var that = {}; that.m = GM.create(id); return that; } return init; }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { return function(id) { var that = {}; that.m = GM.create(id); return that; } }); require("map", function(Map) { require.ready(function() { a = new Map.init("div_id"); b = new Map.init("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { return function(id) { var that = {}; that.m = GM.create(id); return that; } }); require("map", function(Map) { require.ready(function() { a = new Map("div_id"); b = new Map("another_id"); }); }); define(["gm", "foo"], function(GM, Foo) { return function(id) { var that = {}; that.m = GM.create(id); return that; } }); Plugins define([ "foo!bar" ], function() { ... }); When order matters... define(["order!foo", "order!bar", "order!baz"], function(Foo, Bar, Baz) { ... }); Loading a Template define(["resig_micro", "text!bar"], function(Resig, BarTmpl) { ... data = { foo: 1, bar: "once upon a time..." }; rendered = Resig(BarTmpl, data); ... }); Write in CoffeeScript define(["cs!foo"], function(Foo) { ... }); Loading CSS define(["css!foo"], function() { ... }); function loadCss(url) { var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; link.href = url; document.getElementsByTagName("head") [0].appendChild(link); } Loading Offsite content define(["foo", "https://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"], function(Foo, $) { ... }); Loading Google Maps, etc. define(["async!http://maps.google.com/maps/api/js? sensor=false!callback"], function() { return google.maps; }) Getting specific with your settings require({ baseUrl: "/another/path", paths: { "some": "some/v1.0" }, waitSeconds: 15, locale: "fr-fr", context: "foo" }, ["some/module"], function(someModule) { ... // some/v1.0/module.js Compiling / Minifying ./requirejs/build/build.sh app.build.js • app.build.js • dev/ o your_stuff.js • requirejs/ o require.js source files • built/ o destination directory app.build.js ({ appDir: "dev/", baseUrl: "scripts", dir: "built/", optimize: "uglify", ... paths? priority? ... }) github.com/jrburke/r.js/blob/master/build/example.build.js Thanks Chris Pitzer requirejs.org #requirejs on irc.freenode.net