RequireJS

  • 7,054 views
Uploaded on

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

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

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
7,054
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
0
Comments
0
Likes
19

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 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
  • 2. Templates
    Your own sub headline
  • Web sites are turning into Web apps
    Code complexity grows as the site gets bigger
    Assembly gets harder
    Developer wants discrete JS files/modules
    Deployment wants optimized code in just one or a few HTTP calls
    The Problem
  • 8. Script Tag Vomit
    <script src=“http://localhost/AppSite/Scripts/ThirdParty/require.js type=“text/javascript”></script>
    <script src=“http://localhost/AppSite/Scripts/main.js” type=“text/javascript”></script>
    <script src=“http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js " type=“text/javascript”></script>
    <script src=“http://localhost/AppSite/Scripts/Application/controller.js " type=“text/javascript”></script>
    <script src=“http://localhost/AppSite/Scripts/Application/model.js " type=“text/javascript”></script>
    <script src=“http://localhost/AppSite/Scripts/Application/view.js " type=“text/javascript”></script>
    <script src=“http://localhost/AppSite/Scripts/config.js " type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Utilities/utils.js " type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Experian/experian.js " type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/Core/email.js " type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/Core/log.js" type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/Core/Utilities/countdownTimer.js" type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/ThirdParty/jquery.validity.pack-wrapped.js” type=“text/javascript”></script>
    <script src=“http://localhost/myCompany.core.javascript/Scripts/ThirdParty/json2.js” type=“text/javascript”></script>
    <script src=“” http://localhost/myCompany.core.javascript/Scripts/Core/states.js type=“text/javascript”></script>
    http://www.flickr.com/photos/nicasaurusrex/1363221060/
  • 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
  • 11. AMD
    Asynchronous Module Definition
    AMD attempts to address the following problem:
    • Ability to load nested dependenciesasynchronously
    From https://github.com/amdjs/amdjs-api/wiki/AMD:
    The Asynchronous Module Definition API specifies a mechanism for defining modules such that the module and its dependencies can be asynchronously loaded
    The specification defines a single function "define" that is available as a free variable or a global variable:
    define(id?, dependencies?, factory);
    AMD is a specification, not a technology
    AMD is the basis for a number of script loader libraries…
  • 12. AMD
    Implementations
  • RequireJS implements AMD to solve these additional problems:
    Some sort of #include/import/require
    Ease of use for developer backed by an optimization tool that helps deployment
    RequireJS
  • 16. RequireJS
    http://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.
    • Asynchronous script loader
    • 17. Call it anytime
    • 18. Encapsulation
    • 19. Trace nested dependencies
    • 20. Avoid polluting the global namespace
    • 21. Optimization tool for JS/CSS
  • RequireJS
    require()
    Simplest use-case, load a single JS file:
    require([“scripts/myScript.js”], function () {
    //Do something once the script has loaded
    });
  • 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…
  • 23. <script src=“require.js” type=“text/javascript></script>
    <script type=“text/javascript>
    require([
    “Application/controller”,
    “Application/model”,
    “Application/view”,
    “config”,
    “Core/Utilities/utils”,
    “Core/Experian/experian”,
    “Core/Experian/email”
    “Core/Experian/log”
    “Core/Utilities/countdownTimer”,
    …etc…
    ])
    </script>
    RequireJS
    Better Vomit
  • 24. <script src=“require.js” data-main=“main” type=“text/javascript></script>
    More on that later…
    RequireJS
    Best
  • 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. RequireJS
    Module Pattern Benefits
    • Encapsulation
    • 31. Dependency management
    • 32. Programming against interfaces vs. implementations
    • 33. Single responsibility principle
    • 34. Reduced file size
    • 35. Reusability
  • RequireJS
    define()
    Simplest use-case - name/value pairs with no dependencies:
    shirt.js:
    //pass an object literal to define()
    define({
    color: “black”,
    size: “unisize”
    });
    To use this simple module:
    require([“shirt”], function (shirt) {
    //Do something once shirt.js has loaded
    console.log(“Color: “ + shirt.color);
    console.log(“Size: “ + shirt.size);
    });
  • 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);
    }
    }
    });
  • 38. RequireJS
    Module Naming Scheme
    Module names are mapped to directory/file structure* allowing intuitive organization of source files:
    Assuming a base path of “Scripts” the module naming scheme for these modules would be:
    • “Application/controller”
    • 39. “Application/model”
    • 40. “Application/view”
    * This can be changed via the “paths” config, see RequireJS documentation for details
  • 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
  • 49. RequireJS
    Optimizer
    Development
    Deployment
  • 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
  • 54. Build file:
    app.build.js:
    ({
    //config options excluded for brevity…
    modules: [
    {
    name: "appCommon“ //optimize appCommon by itself
    },
    {
    name: “page1",
    exclude: ["appCommon"] //exclude appCommon from optimized “page1.js” file
    },
    {
    name: “page2",
    exclude: ["appCommon"] //exclude appCommon from optimized “page2.js” file
    }
    ]
    })
    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
  • 58. RequireJS
    Optimizer - Shared Code - Build
    Main.js
    Main.js - optimized
    File System
    Shared Code
    Optimizer
  • 59. RequireJS
    Optimizer - Shared Code - Deployment
    Main.js
    App Site
    File System
  • 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/
  • 62. RequireJS
    Hello World
    hello.js:
    define({
    text: "Hello"
    });
  • 63. RequireJS
    Hello World
    world.js:
    define({
    text: “World!"
    });
  • 64. RequireJS
    Hello World
    main.js:
    require(
    ["hello", "world"],
    function(hello, world){
    alert(hello.text + " " + world.text);
    }
    );
  • 65. RequireJS
    Hello World
    index.html:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title></title>
    </head>
    <body>
    <script src="scripts/require.js" data-main="scripts/main" type="text/javascript"></script>
    </body>
    </html>
  • 66. RequireJS
    Hello World - Development
  • 67. RequireJS
    Hello World - Development
    Script tags inserted into the head on-demand:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title></title>
    <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="main" src="scripts/main.js">
    <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="hello" src="scripts/hello.js">
    <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="world" src="scripts/world.js">
    </head>
    <body>
    <script type="text/javascript" data-main="scripts/main" src="scripts/require.js">
    </body>
    </html>
  • 68. RequireJS
    Hello World - Build
    app.build.js:
    ({
    appDir: "../",
    baseUrl: "scripts",
    dir: "../Build",
    paths: {},
    optimize: "uglify",
    modules: [
    { name: "main“ }
    ]
    })
  • 69. RequireJS
    Hello World - Build
    Build: (Win32 command prompt with Node)
    C:>
    C:>cd <projectpath>scripts
    C:<projectpath>scripts>
    C:<projectpath>scripts>node <requirejspath>r.js -o app.build.js
  • 70. RequireJS
    Hello World - Build
    <projectpath>Buildscriptsmain.js:
    define(“hello",{text:"Hello"}),define(“world",{text:"World!"}),require([“hello",“world"],function(a,b){alert(a.text+" "+b.text)}),define("main",function(){})
  • 71. RequireJS
    Hello World - Production
  • 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.