Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.


An overview of James Burke's RequireJS script/module loader

  • Login to see the comments


  1. 1. RequireJS<br />Adventures with an asynchronous script loader<br />Some content borrowed from and<br />Tim Doherty<br />
  2. 2. Templates<br />Your own sub headline<br /><ul><li>The Problem
  3. 3. Script Tag Vomit
  4. 4. Solution
  5. 5. AMD
  6. 6. RequireJS
  7. 7. Conclusion</li></li></ul><li>Web sites are turning into Web apps<br />Code complexity grows as the site gets bigger<br />Assembly gets harder<br />Developer wants discrete JS files/modules<br />Deployment wants optimized code in just one or a few HTTP calls<br />The Problem<br />
  8. 8. Script Tag Vomit<br /><script src=“http://localhost/AppSite/Scripts/ThirdParty/require.js type=“text/javascript”></script><br /><script src=“http://localhost/AppSite/Scripts/main.js” type=“text/javascript”></script><br /><script src=“ " type=“text/javascript”></script><br /><script src=“http://localhost/AppSite/Scripts/Application/controller.js " type=“text/javascript”></script><br /><script src=“http://localhost/AppSite/Scripts/Application/model.js " type=“text/javascript”></script><br /><script src=“http://localhost/AppSite/Scripts/Application/view.js " type=“text/javascript”></script><br /><script src=“http://localhost/AppSite/Scripts/config.js " type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Utilities/utils.js " type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Experian/experian.js " type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/Core/email.js " type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/Core/log.js" type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Utilities/countdownTimer.js" type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/ThirdParty/jquery.validity.pack-wrapped.js” type=“text/javascript”></script><br /><script src=“http://localhost/myCompany.core.javascript/Scripts/ThirdParty/json2.js” type=“text/javascript”></script><br /><script src=“” http://localhost/myCompany.core.javascript/Scripts/Core/states.js type=“text/javascript”></script><br /><br />
  9. 9. Slow<br />Many HTTP Requests<br />Blocking render<br />Manual Dependecies<br />Lacks Encapsulation<br />Script Tag Vomit<br /><br />
  10. 10. Front-end developers need a solution with:<br />Some sort of #include/import/require<br />Ability to load nested dependencies asynchronously<br />Ease of use for developer backed by an optimization tool that helps deployment<br />Solution<br />
  11. 11. AMD<br />Asynchronous Module Definition<br />AMD attempts to address the following problem:<br /><ul><li> Ability to load nested dependenciesasynchronously</li></ul>From<br />The Asynchronous Module Definition API specifies a mechanism for defining modules such that the module and its dependencies can be asynchronously loaded<br />The specification defines a single function "define" that is available as a free variable or a global variable:<br /> define(id?, dependencies?, factory);<br />AMD is a specification, not a technology<br />AMD is the basis for a number of script loader libraries…<br />
  12. 12. AMD<br />Implementations<br /><ul><li>RequireJS
  13. 13. Nodules
  14. 14. JSLocalnet
  15. 15. Curl.js</li></li></ul><li>RequireJS implements AMD to solve these additional problems:<br />Some sort of #include/import/require<br />Ease of use for developer backed by an optimization tool that helps deployment<br />RequireJS<br />
  16. 16. RequireJS<br /><br />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.<br /><ul><li> Asynchronous script loader
  17. 17. Call it anytime
  18. 18. Encapsulation
  19. 19. Trace nested dependencies
  20. 20. Avoid polluting the global namespace
  21. 21. Optimization tool for JS/CSS</li></li></ul><li>RequireJS<br />require()<br />Simplest use-case, load a single JS file:<br />require([“scripts/myScript.js”], function () {<br />//Do something once the script has loaded<br />});<br />
  22. 22. RequireJS<br />require()<br />require() can load an array of AMD modules as dependencies: <br />require(<br /> [<br /> // array of dependencies<br /> “dependency1“,<br /> “dependency2” <br /> ],<br />function (dependency1, dependency2) {<br /> // use dependencies<br /> dependency1.someMethod();<br />…etc…<br /> }<br />);<br />More on that later…<br />
  23. 23. <script src=“require.js” type=“text/javascript></script><br /><script type=“text/javascript><br /> require([<br /> “Application/controller”,<br /> “Application/model”,<br /> “Application/view”,<br /> “config”,<br /> “Core/Utilities/utils”,<br /> “Core/Experian/experian”,<br /> “Core/Experian/email”<br /> “Core/Experian/log”<br /> “Core/Utilities/countdownTimer”,<br /> …etc…<br /> ])<br /></script><br />RequireJS<br />Better Vomit<br />
  24. 24. <script src=“require.js” data-main=“main” type=“text/javascript></script><br />More on that later…<br />RequireJS<br />Best<br />
  25. 25. RequireJS creates script tags on demand and adds them to the head:<br />var head = document.getElementsByTagName('head')[0],<br /> script = document.createElement('script');<br />script.src = url;<br />head.appendChild(script);<br />This approach has the following advantages:<br />Will not block page rendering <br />Works after page load<br />Not constrained by same origin policy (SOP)<br />RequireJS<br />Loading Scripts<br />
  26. 26. RequireJS<br />Loading Scripts<br />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: <br />define(<br />//The name of this module<br /> “module1",<br />//The array of dependencies<br /> [“dependency1"], //filename without “.js” indicates a module<br />//The function to execute when all dependencies have loaded. The arguments<br /> //to this function are the array of dependencies mentioned above.<br />function (dependency1) {<br />return {<br /> method1: function() {<br />//Use the dependency<br /> dependency1.someMethod();<br /> }<br /> };<br /> }<br />);<br />
  27. 27. RequireJS<br />JavaScript Module Pattern<br />(function () { <br />//Do setup work here<br />return { <br /> color: "black", <br /> size: "unisize" <br /> } <br />})();<br />
  28. 28. RequireJS<br />RequireJS Module Pattern<br />define(function () { <br />//Do setup work here<br />return { <br /> color: "black", <br /> size: "unisize" <br /> } <br />});<br />
  29. 29. RequireJS<br />RequireJS Module Pattern<br />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<br />Extension of the widely used JS module pattern<br />Minimal boilerplate code for developers to adopt<br />Defines a well-scoped object that avoids polluting the global namespace<br />Explicitly lists its dependencies<br />Receives dependencies as arguments to the function that defines the module<br />Does not need globals to refer to other modules<br />
  30. 30. RequireJS<br />Module Pattern Benefits<br /><ul><li> Encapsulation
  31. 31. Dependency management
  32. 32. Programming against interfaces vs. implementations
  33. 33. Single responsibility principle
  34. 34. Reduced file size
  35. 35. Reusability</li></li></ul><li>RequireJS<br />define()<br />Simplest use-case - name/value pairs with no dependencies:<br />shirt.js:<br />//pass an object literal to define()<br />define({<br /> color: “black”,<br /> size: “unisize”<br />});<br />To use this simple module:<br />require([“shirt”], function (shirt) {<br />//Do something once shirt.js has loaded<br /> console.log(“Color: “ + shirt.color);<br /> console.log(“Size: “ + shirt.size);<br />});<br />
  36. 36. RequireJS<br />define()<br />Definition functions - use a function to do setup work then define the module:<br />shirt.js:<br />//pass a function that does setup work <br />//before returning a module definition. <br />define(function () { <br />//Do setup work here <br /> //return an object to define the module<br />return { <br /> color: "black", <br /> size: "unisize" <br /> } <br />});<br />
  37. 37. RequireJS<br />define()<br />Definition functions with dependencies - use dependencies and a function with dependency arguments to do setup work then define the module:<br />shirt.js:<br />//pass an array of dependencies, and a function that<br />//does setup work before returning a module definition. <br />define(["./cart", "./inventory"] ,function (cart, inventory) { <br />//Do setup work here <br /> //return an object to define the module<br />return { <br /> color: "black", <br /> size: "unisize“,<br />addToCart: function() {<br />//use the dependencies in the module definition<br />inventory.decrement(this); <br />cart.add(this); <br /> } <br /> } <br />});<br />
  38. 38. RequireJS<br />Module Naming Scheme<br />Module names are mapped to directory/file structure* allowing intuitive organization of source files:<br />Assuming a base path of “Scripts” the module naming scheme for these modules would be:<br /><ul><li>“Application/controller”
  39. 39. “Application/model”
  40. 40. “Application/view”</li></ul>* This can be changed via the “paths” config, see RequireJS documentation for details<br />
  41. 41. RequireJS<br />Dependency Management<br />Modules can specify their dependencies explicitly:<br />Dependencies can be nested<br />Load once, use in multiple locations<br />Plugins for text dependencies, load order, etc.<br />Compatible with JavaScript prototypal inheritance<br />
  42. 42. RequireJS<br />Prototypal Inheritance<br />Inject prototypes as dependencies and return constructor functions from our modules to create new objects<br />define(<br />//The name of this module<br /> "types/Manager",<br />//The array of dependencies<br /> ["types/Employee"],<br />//The function to execute when all dependencies have loaded. The arguments<br /> //to this function are the array of dependencies mentioned above.<br />function (Employee) {<br />function Manager () {<br />this.reports = [];<br /> }<br /> //This will now work<br />Manager.prototype = new Employee();<br /> //return the Manager constructor function so it can be used by other modules.<br />return Manager;<br /> }<br />);<br />
  43. 43. RequireJS<br />Object Composition<br />Expose dependencies in a module’s return value to create new objects by composition<br />define(<br />//The array of dependencies<br /> [“dependency1"],<br /> [“dependency2"]<br />//The function to execute when all dependencies have loaded. The arguments<br /> //to this function are the array of dependencies mentioned above.<br />function (dependency1, dependency2) {<br /> //do setup work<br /> //expose dependencies as properties of returned object <br />return {<br />//module-defined code<br /> …<br /> composedMethod1: dependency1.someMethod,<br /> composedObject2: dependency2,<br /> };<br /> }<br />);<br />
  44. 44. RequireJS<br />Text Dependencies<br />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.<br />require(["some/module", "text!some/module.html", "text!some/module.css"], <br />function(module, html, css) { <br /> //the html variable will be the text <br /> //of the some/module.html file <br /> //the css variable will be the text <br /> //of the some/module.css file. <br /> } <br />);<br />At build time, the optimizer will inline these text dependencies in the resulting optimized file for deployment.<br />
  45. 45. RequireJS<br />Page Load Support<br />The require.ready() method allows a callback when the DOM is <br />ready AND all dependencies have been loaded:<br />require(["module/one", "module/two"],<br />function(one, two) { <br />require.ready(function() { <br />//DOM is ready AND all dependencies loaded<br />one.modifyTheDom(); <br /> }); <br /> } <br />);<br />This is similar to the document.ready() method in jQuery.<br />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.<br />
  46. 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:<br /><script src=“require.js” data-main=“main” type=“text/javascript></script><br />Main.js:<br />require([“scripts/myScript.js”], function () {<br />//Do something once the script has loaded<br />});<br />The data-main attribute tells RequireJS to take the value of the data-main attribute and treat it like a require([]) call<br />RequireJS<br />Data-main<br />
  47. 47. RequireJS includes an optimization tool to help deployment<br />Combine modules into fewer files for deployment<br />In some cases, this could be a single file!<br />Java & node versions<br />Concatenate & minify JS files<br />Shallow file exclusion for tweaking/debugging individual JS files<br />Concatenate CSS @import files<br />Minify CSS files<br />File system vs. URLs<br />RequireJS<br />Optimizer<br />
  48. 48. Build File:<br />({<br />appDir: "../", //main application directory<br />baseUrl: "Scripts", //path, relative to “dir”, where RequireJS should start looking for modules<br /> dir: "../../Build", //build output directory<br /> paths: { //optional path aliases<br /> "jQuery": "empty",<br /> "convert": "conversionFunctions",<br /> },<br /> optimize: "uglify", //"uglify", "closure" (Java Only), "closure.keepLines" (Java Only), "none"<br /> modules: [ //array of modules to optimize<br /> {<br /> name: "main",<br /> exclude: ["jQuery", "config"] //exclude these dependencies from the optimized “main.js” file<br /> }<br /> ]<br />})<br />RequireJS<br />Optimizer<br />
  49. 49. RequireJS<br />Optimizer<br />Development<br />Deployment<br />
  50. 50. For multi-page applications, layered optimization may make sense:<br />Layer(s) for common application code, including 3rd-party libraries<br />Layer for page-specific scripts<br />RequireJS<br />Optimizer – Layers <br />
  51. 51. Common code:<br />appCommon.js:<br />define(<br /> [ <br /> "../../Scripts/commonFunctions",<br /> "../../Scripts/form-utils“,<br /> "../../Scripts/jquery-1.4.1.min.js",<br /> "order!../../Scripts/datepicker.js",<br /> "order!../../Scripts/jquery.validate.min.js“,<br /> …etc…<br /> ],<br />function (commonFunctions, form-utils) {<br />//create common code module definition<br /> }<br />);<br />RequireJS<br />Optimizer – Layers<br />
  52. 52. Page 1 module:<br />page1.js:<br />require(["appCommon"],<br /> function() {<br /> //code specific to page 1<br /> }<br />);<br />RequireJS<br />Optimizer – Layers<br />
  53. 53. Page 2 module:<br />page2.js:<br />require(["appCommon"],<br /> function() {<br /> //code specific to page 2<br /> }<br />);<br />RequireJS<br />Optimizer – Layers<br />
  54. 54. Build file:<br /><br />({<br />//config options excluded for brevity…<br /> modules: [<br /> { <br /> name: "appCommon“ //optimize appCommon by itself<br /> },<br /> {<br /> name: “page1",<br /> exclude: ["appCommon"] //exclude appCommon from optimized “page1.js” file<br /> },<br /> {<br /> name: “page2",<br /> exclude: ["appCommon"] //exclude appCommon from optimized “page2.js” file<br /> }<br /> ]<br />})<br />RequireJS<br />Optimizer – Layers<br />
  55. 55. The build output will be:<br />Optimized appCommon.js file<br />Optimized page1.js file with optimized appCommon.js as a dependency<br />Optimized page2.js file with optimized appCommon.js as a dependency<br />RequireJS<br />Optimizer – Layers<br />
  56. 56. Development– use a local website hosting the shared code:<br />paths: {<br /> "ThirdParty": “http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty",<br /> "core": "http://localhost/myCompany.Core.Javascript/Scripts/Core",<br /> "text": "http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty/text",<br /> "order": "http://localhost/myCompany.Core.Javascript/Scripts/ThirdParty/order“<br />},<br />Build – pull shared code from corresponding file paths:<br />paths: {<br /> "ThirdParty": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty",<br /> "core": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/Core",<br /> "text": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty/text",<br /> "order": "../../../myCompany.Core/myCompany.Core.Javascript/Scripts/ThirdParty/order“<br />},<br />During development, modules are loaded async from a single, version-controlled project<br />During build, modules are copied from the file system and combined into a single file to be deployed with the target project<br />RequireJS<br />Optimizer - Shared Code<br />
  57. 57. RequireJS<br />Optimizer - Shared Code - Development<br />App Site<br />Main.js<br />File System<br />Shared Code<br />Shared Code Site<br />
  58. 58. RequireJS<br />Optimizer - Shared Code - Build<br />Main.js<br />Main.js - optimized<br />File System<br />Shared Code<br />Optimizer<br />
  59. 59. RequireJS<br />Optimizer - Shared Code - Deployment<br />Main.js<br />App Site<br />File System<br />
  60. 60. RequireJS<br />Optimizer – Visual Studio Integration<br />Add build commands to post-build events:<br />
  61. 61. RequireJS<br />Hello World<br />Hello World sample application<br />hello.js: defines an object with a single property “text” = “Hello”<br />world.js: defines an object with a single property “text” = “World!”<br />main.js: require’s “hello” and “world” and outputs “text” properties<br />index.html: script tag for require.js with data-main attribute for main.js<br /> build file<br /><br />
  62. 62. RequireJS<br />Hello World<br />hello.js:<br />define({<br /> text: "Hello"<br />});<br />
  63. 63. RequireJS<br />Hello World<br />world.js:<br />define({<br /> text: “World!"<br />});<br />
  64. 64. RequireJS<br />Hello World<br />main.js:<br />require(<br /> ["hello", "world"],<br />function(hello, world){<br /> alert(hello.text + " " + world.text);<br /> }<br />);<br />
  65. 65. RequireJS<br />Hello World<br />index.html:<br /><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"<br /> ""><br /><html><br /> <head><br /> <title></title><br /> </head><br /> <body><br /> <script src="scripts/require.js" data-main="scripts/main" type="text/javascript"></script><br /> </body><br /></html><br />
  66. 66. RequireJS<br />Hello World - Development<br />
  67. 67. RequireJS<br />Hello World - Development<br />Script tags inserted into the head on-demand:<br /><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ""><br /><html><br /> <head><br /> <title></title><br /> <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="main" src="scripts/main.js"><br /> <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="hello" src="scripts/hello.js"><br /> <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="world" src="scripts/world.js"><br /> </head><br /> <body><br /> <script type="text/javascript" data-main="scripts/main" src="scripts/require.js"><br /> </body><br /></html><br />
  68. 68. RequireJS<br />Hello World - Build<br /><br />({<br />appDir: "../",<br />baseUrl: "scripts",<br /> dir: "../Build",<br /> paths: {},<br /> optimize: "uglify",<br /> modules: [<br /> { name: "main“ }<br /> ]<br />})<br />
  69. 69. RequireJS<br />Hello World - Build<br />Build: (Win32 command prompt with Node)<br />C:> <br />C:>cd <projectpath>scripts<br />C:<projectpath>scripts><br />C:<projectpath>scripts>node <requirejspath>r.js -o<br />
  70. 70. RequireJS<br />Hello World - Build<br /><projectpath>Buildscriptsmain.js:<br />define(“hello",{text:"Hello"}),define(“world",{text:"World!"}),require([“hello",“world"],function(a,b){alert(a.text+" "+b.text)}),define("main",function(){})<br />
  71. 71. RequireJS<br />Hello World - Production<br />
  72. 72. Stop vomiting script tags!<br />AMD is gaining traction<br />AMD brings sanity to JavaScript development<br />Structure, encapsulation, dependency management <br />Write once, use anywhere<br />RequireJS is the most popular AMD script loader<br />RequireJS likely to be part of Dojo 2.0<br />RequireJS nearing 1.0 release<br />Open source, active community<br />Conclusion<br />
  73. 73. RequireJS<br />Adventures with an asynchronous script loader<br /><br /><br /><br /> <br /><br /><br />RequireJS is an open source project authored by James Burke, open source developer, RequireJS, Mozilla Messaging, and Dojo. <br />