JS Modules
class: center, middle .title[ Front-end training # Modules in JS ] --- # Why? Because! - Pollution of global scope - Name clashes and conflicts - Implicit dependencies, need to be loaded in correct order - No separation of concerns - Hard to maintain - Hard to expand - Hard to test --- # Too many type of modules... - Object literal notation - Module pattern - AMD - CommonJS - Harmony modules --- # Object literal ``` js var myObjectLiteral = { variableKey: variableVAlue, functionKey: function (){...} }; ``` --- # Module Pattern ```js var counter = (function () { var counter = 0; return { increment: function () { return counter++; }, reset: function () { counter = 0; } }; })(); ``` --- # Tons of files! Writing modular code will generate lots of files and that’s actually ok! ``` html ... ... ``` But browser assumes you load files in right order :(  --- # Real modules! - Module format - Loader - Build tools (optimizers) - Ease of development and debugging --- # AMD Designed with browser environment in mind to allow/promote asynchronous loading ``` js // define(id?, dependencies?, factory); define (['config'], function (config){ var privateVar = config.counter; // expose public API return { increase: function () {...} } }); ``` Plugins for loading something other than js ```js // [Plugin Module ID] ! [resource ID] define (['text!../templ/start.html'], function (template) { // do something with loaded html }); ``` --- # CommonJS A community driven set of proposed best practices, specs and APIs, with a goal of building up the JS ecosystem across environments. - The scope is limited to the file - “free” variables `require, module, exports` ```js // math.js export.add = function () { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l){ sum += args[i++]; } return sum; }; // another.js var math = require('math'); exports.increment = function(val){ return math.add(val, 1); }; ``` --- # AMD + CommonJS ? Simplified CommonJS wrapping ```js define (function (require, exports, module){ var math = require ('math'); exports.increment = function (val) { return math.add (val, 1); } }); ``` --- # Loaders Basic example ```js function loadJS(file) { // DOM: Create the script element var jsElm = document.createElement("script"); // set the type attribute jsElm.type = "application/javascript"; // make the script element load file jsElm.src = file; // finally insert the element to the body element in order to load the script document.body.appendChild(jsElm); } ``` Popular AMD & CommonJS loaders: [RequireJS](http://requirejs.org) [curl.js](https://github.com/cujojs/curl) --- # RequireJS ``` html ``` ```js require.config({ baseUrl: '/some/path', // The shim config allows us to configurate dependencies // for scripts that do not call define() to register a module shim: { underscore: { exports: '_' }, backbone: { deps: ['underscore', 'jquery'], exports: 'Backbone' } }, // Require.js allows us to configurate shortcut alias paths: { jquery: '../bower_components/jquery/jquery' } }); ``` --- # Build Tools Follows the dependency chain specified in the define() and require() calls, concatenates those into one or few files. Specify which top-level modules or resources are in each file and the build tool finds the rest. - [r.js (requirejs optimizer)](http://requirejs.org/docs/optimization.html) - [browserify](https://github.com/substack/node-browserify) --- # Browserify Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node. ``` bash $ npm install -g browserify $ browserify main.js -o build.js ``` Building app every time you change file might be slow But there's an optimized plugin to do that really fast ``` bash $ npm install -g watchify $ watchify main.js -o build.js ``` Debug with sourcemaps ``` bash $ browserify main.js -- outfile build.js --debug ``` --- # ES Harmony ```js // lib/math.js export function sum(x, y) { return x + y; } export var pi = 3.141593; // app.js module math from "lib/math"; alert("2π = " + math.sum(math.pi, math.pi)); // otherApp.js import {sum, pi} from "lib/math"; alert(""2π = " + sum(pi, pi)); // load from remote sources module $ from "http://whateva.org/jquery.js" // Module loader API Loader.load("http://whateva.org/jquery.js", function($){ ... }); ``` --- # Links .litle[ Mustread: http://addyosmani.com/writing-modular-js Specs: http://wiki.commonjs.org/wiki/Modules https://github.com/amdjs/amdjs-api/blob/master/AMD.md http://wiki.ecmascript.org/doku.php?id=harmony:modules Holywars: http://unscriptable.com/2011/09/30/amd-versus-cjs-whats-the-best-format - 2011 http://blog.millermedeiros.com/amd-is-better-for-the-web-than-commonjs-modules - 2011 http://derickbailey.com/2014/06/10/browserify-my-new-choice-for-modules-in-a-browser-backbone-app - 2014 Tools: http://requirejs.org ~7.8k stars http://browserify.org ~7.9k stars https://github.com/cujojs/curl ~1.5k stars https://github.com/google/traceur-compiler https://github.com/ModuleLoader/es6-module-loader Practice: http://code.tutsplus.com/tutorials/a-requirejs-backbone-and-bower-starter-template--net-29211 http://javascriptplayground.com/blog/2014/06/es6-modules-today/ http://mikefowler.me/2014/06/11/backbone-with-es6/ http://tagtree.tv/ecmascript-6-episode-3 ]