1. RequireJS Adventures with an asynchronous script loader Some content borrowed from http://www.tagneto.org/talks/jQueryRequireJS/jQueryRequireJS.pdf and http://requirejs.org Tim Doherty
9. Slow Many HTTP Requests Blocking render Manual Dependecies Lacks Encapsulation Script Tag Vomit http://www.flickr.com/photos/nicasaurusrex/1363221060/
10. Front-end developers need a solution with: Some sort of #include/import/require Ability to load nested dependencies asynchronously Ease of use for developer backed by an optimization tool that helps deployment Solution
22. RequireJS require() require() can load an array of AMD modules as dependencies: require( [ // array of dependencies “dependency1“, “dependency2” ], function (dependency1, dependency2) { // use dependencies dependency1.someMethod(); …etc… } ); More on that later…
25. RequireJS creates script tags on demand and adds them to the head: var head = document.getElementsByTagName('head')[0], script = document.createElement('script'); script.src = url; head.appendChild(script); This approach has the following advantages: Will not block page rendering Works after page load Not constrained by same origin policy (SOP) RequireJS Loading Scripts
26. RequireJS Loading Scripts However, we need to know the dependencies and make sure we load them before executing our script. The best way to do that is to use function wrappers. We pass function wrappers to the define() method, to create RequireJS modules: define( //The name of this module “module1", //The array of dependencies [“dependency1"], //filename without “.js” indicates a module //The function to execute when all dependencies have loaded. The arguments //to this function are the array of dependencies mentioned above. function (dependency1) { return { method1: function() { //Use the dependency dependency1.someMethod(); } }; } );
27. RequireJS JavaScript Module Pattern (function () { //Do setup work here return { color: "black", size: "unisize" } })();
28. RequireJS RequireJS Module Pattern define(function () { //Do setup work here return { color: "black", size: "unisize" } });
29. RequireJS RequireJS Module Pattern The RequireJS syntax for modules allows them to be loaded as fast as possible, even out of order, but evaluated in the correct dependency order, and since global variables are not created, it makes it possible to load multiple versions of a module in a page Extension of the widely used JS module pattern Minimal boilerplate code for developers to adopt Defines a well-scoped object that avoids polluting the global namespace Explicitly lists its dependencies Receives dependencies as arguments to the function that defines the module Does not need globals to refer to other modules
30.
31. Dependency management
32. Programming against interfaces vs. implementations
33. Single responsibility principle
34. Reduced file size
35.
36. RequireJS define() Definition functions - use a function to do setup work then define the module: shirt.js: //pass a function that does setup work //before returning a module definition. define(function () { //Do setup work here //return an object to define the module return { color: "black", size: "unisize" } });
37. RequireJS define() Definition functions with dependencies - use dependencies and a function with dependency arguments to do setup work then define the module: shirt.js: //pass an array of dependencies, and a function that //does setup work before returning a module definition. define(["./cart", "./inventory"] ,function (cart, inventory) { //Do setup work here //return an object to define the module return { color: "black", size: "unisize“, addToCart: function() { //use the dependencies in the module definition inventory.decrement(this); cart.add(this); } } });
41. RequireJS Dependency Management Modules can specify their dependencies explicitly: Dependencies can be nested Load once, use in multiple locations Plugins for text dependencies, load order, etc. Compatible with JavaScript prototypal inheritance
42. RequireJS Prototypal Inheritance Inject prototypes as dependencies and return constructor functions from our modules to create new objects define( //The name of this module "types/Manager", //The array of dependencies ["types/Employee"], //The function to execute when all dependencies have loaded. The arguments //to this function are the array of dependencies mentioned above. function (Employee) { function Manager () { this.reports = []; } //This will now work Manager.prototype = new Employee(); //return the Manager constructor function so it can be used by other modules. return Manager; } );
43. RequireJS Object Composition Expose dependencies in a module’s return value to create new objects by composition define( //The array of dependencies [“dependency1"], [“dependency2"] //The function to execute when all dependencies have loaded. The arguments //to this function are the array of dependencies mentioned above. function (dependency1, dependency2) { //do setup work //expose dependencies as properties of returned object return { //module-defined code … composedMethod1: dependency1.someMethod, composedObject2: dependency2, }; } );
44. RequireJS Text Dependencies It is nice to build HTML using regular HTML tags, instead of building up DOM structures in script. However, there is no good way to embed HTML in a JavaScript file. RequireJS has a plugin, text.js, that can help with this issue. require(["some/module", "text!some/module.html", "text!some/module.css"], function(module, html, css) { //the html variable will be the text //of the some/module.html file //the css variable will be the text //of the some/module.css file. } ); At build time, the optimizer will inline these text dependencies in the resulting optimized file for deployment.
45. RequireJS Page Load Support The require.ready() method allows a callback when the DOM is ready AND all dependencies have been loaded: require(["module/one", "module/two"], function(one, two) { require.ready(function() { //DOM is ready AND all dependencies loaded one.modifyTheDom(); }); } ); This is similar to the document.ready() method in jQuery. However, since it also ensures that all dependencies are loaded, require.ready() should be used in place of jQuery’sdocument.ready in any project using both jQuery and RequireJS.
46. While you can use require() inside a script tag in an HTML file, it is strongly encouraged to place the work in a file that is loaded by RequireJS. This allows for easier optimization via the optimization tool, and there is a shorthand that can be used in the HTML for this pattern: <script src=“require.js” data-main=“main” type=“text/javascript></script> Main.js: require([“scripts/myScript.js”], function () { //Do something once the script has loaded }); The data-main attribute tells RequireJS to take the value of the data-main attribute and treat it like a require([]) call RequireJS Data-main
47. RequireJS includes an optimization tool to help deployment Combine modules into fewer files for deployment In some cases, this could be a single file! Java & node versions Concatenate & minify JS files Shallow file exclusion for tweaking/debugging individual JS files Concatenate CSS @import files Minify CSS files File system vs. URLs RequireJS Optimizer
48. Build File: ({ appDir: "../", //main application directory baseUrl: "Scripts", //path, relative to “dir”, where RequireJS should start looking for modules dir: "../../Build", //build output directory paths: { //optional path aliases "jQuery": "empty", "convert": "conversionFunctions", }, optimize: "uglify", //"uglify", "closure" (Java Only), "closure.keepLines" (Java Only), "none" modules: [ //array of modules to optimize { name: "main", exclude: ["jQuery", "config"] //exclude these dependencies from the optimized “main.js” file } ] }) RequireJS Optimizer
50. For multi-page applications, layered optimization may make sense: Layer(s) for common application code, including 3rd-party libraries Layer for page-specific scripts RequireJS Optimizer – Layers
51. Common code: appCommon.js: define( [ "../../Scripts/commonFunctions", "../../Scripts/form-utils“, "../../Scripts/jquery-1.4.1.min.js", "order!../../Scripts/datepicker.js", "order!../../Scripts/jquery.validate.min.js“, …etc… ], function (commonFunctions, form-utils) { //create common code module definition } ); RequireJS Optimizer – Layers
52. Page 1 module: page1.js: require(["appCommon"], function() { //code specific to page 1 } ); RequireJS Optimizer – Layers
53. Page 2 module: page2.js: require(["appCommon"], function() { //code specific to page 2 } ); RequireJS Optimizer – Layers
55. The build output will be: Optimized appCommon.js file Optimized page1.js file with optimized appCommon.js as a dependency Optimized page2.js file with optimized appCommon.js as a dependency RequireJS Optimizer – Layers
56. Development– use a local website hosting the shared code: paths: { "ThirdParty": “http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty", "core": "http://localhost/myCompany.Core.Javascript/Scripts/Core", "text": "http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty/text", "order": "http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty/order“ }, Build – pull shared code from corresponding file paths: paths: { "ThirdParty": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty", "core": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/Core", "text": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty/text", "order": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty/order“ }, During development, modules are loaded async from a single, version-controlled project During build, modules are copied from the file system and combined into a single file to be deployed with the target project RequireJS Optimizer - Shared Code
57. RequireJS Optimizer - Shared Code - Development App Site Main.js File System Shared Code Shared Code Site
60. RequireJS Optimizer – Visual Studio Integration Add build commands to post-build events:
61. RequireJS Hello World Hello World sample application hello.js: defines an object with a single property “text” = “Hello” world.js: defines an object with a single property “text” = “World!” main.js: require’s “hello” and “world” and outputs “text” properties index.html: script tag for require.js with data-main attribute for main.js app.build.js: build file http://www.flickr.com/photos/oskay/472097903/
72. Stop vomiting script tags! AMD is gaining traction AMD brings sanity to JavaScript development Structure, encapsulation, dependency management Write once, use anywhere RequireJS is the most popular AMD script loader RequireJS likely to be part of Dojo 2.0 RequireJS nearing 1.0 release Open source, active community Conclusion
73. RequireJS Adventures with an asynchronous script loader http://requirejs.org https://github.com/jrburke/r.js http://tagneto.blogspot.com/ https://github.com/amdjs/amdjs-api/wiki/AMD http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth http://javascriptweblog.wordpress.com/2010/06/07/understanding-javascript-prototypes/ RequireJS is an open source project authored by James Burke, open source developer, RequireJS, Mozilla Messaging, and Dojo.