RequireJS – RequireJS is a Javascript file and module loader. – Why should I use RequireJS ?



RequireJS – RequireJS is a Javascript file and module loader. – Why should I use RequireJS ?

0 0


requirejs-slides

A presentation requirejs

On Github ifahrentholz / requirejs-slides

RequireJS

RequireJS is a Javascript file and module loader.

Created by Ingo Fahrentholz

Why should I use RequireJS ?

  • Code organization
  • Code scaleability
  • Code optimization
  • Multi-developer teams
  • Nested dependencies
  • Ease of use vs. compilation
  • Asynchronous
  • Implementation of the AMD specification. You can also use something like Curl.js.

AMD

  • AMD = Asynchronous Module Definition
  • Modules & dependencies are loaded asynchronously.
  • Has a plugin system for different types of modules.
  • Needs an implementation (script loader) to run.
  • Great Adoption (jQuery, MooTools, Dojo, Firebug)

What do I need to use RequireJS ?

<!-- data-main attribute tells require.js to load
scripts/main.js after require.js loads. -->
<script data-main="scripts/main" src="scripts/require.js"></script>

What is RequireJS doing ?

  • It defines two global functions - define() & require()
  • It loads the main.js file (async) after require.js has been loaded

Which responsibility has the main.js file ?

  • The main.js file is the entry point of the whole app
  • It requires the core modules for the website
  • It can define aliases for common used libs
  • It can be used to compile (minimize)

How to define modules

without dependencies

//File: bundle/breakpoints.js
define(function(){
  //Do setup work here
  return {
    phone: 400,
    tablet: 600,
    desktop: 800
  }
});

//Filename: app.js
require(['libs/jquery, bundle/breakpoints'], function($, bp){
  if($('window').width() === bp.tablet) { .. }
});

How to define modules

with dependencies

//File: bundle/breakpoints.js
define(['moduleXY'], function(moduleXY){
  moduleXY.init();

  return {
    phone: 400,
    tablet: 600,
    desktop: 800
  }

});

//Filename: app.js
require(['libs/jquery, bundle/breakpoints'], function($, bp){
  if($('window').width() === bp.tablet) { .. }
});

How to require modules

require(['js/jquery', 'js/bxSlider'], function($, bxSlider){
  $('#element').bxSlider();
});

How to require modules

  • Lazy load modules via the magic module name: require
require(['require', 'js/jquery'], function(require, $){
  ..
  $('#chat-btn').on('click', function(){
    require(['js/chat'], function(Chat){
      //Instatiate and use the Chat App only if the user
      //really want to use it.
    });
  });
  ..
});

Require configuration

  • Configuration of the Require.js Loader
  • Plugin configuration
  • Paths, shims and more

Require configuration - Paths

requirejs.config({
  //By default load any module IDs from js/libs
  baseUrl: 'js/libs',
  //except, if the module ID starts with "app",
  //load it from the js/app directory. paths
  //config is relative to the baseUrl, and
  //never includes a ".js" extension since
  //the paths config could be for a directory.
  paths: {
    app: '../app',
  }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function   ($,        canvas,   sub) {
});

Require configuration - Shims

If the code doesn't support AMD (no define inside of the code)

require.config({                           
  //Remember: only use shim config for non-AMD scripts,
  //scripts that do not already call define(). The shim
  //config will not work correctly if used on AMD scripts,
  //in particular, the exports and init config will not
  //be triggered, and the deps config will be confusing
  //for those cases.
  shim: {
    'backbone': {
      // These script dependencies should be loaded before
      // loading backbone.js
      deps: ['underscore', 'jquery'],

      // Once loaded, use the global 'Backbone' as the 
      // module value.
      exports: 'Backbone'
    }
  }
});

Require configuration - Maps

If you need to use a new API but dont want to break legacy code or you don't have the time to update all modules at once

require.config({
  map: {
    // When some/newmodule requires foo it gets the newer version.
    'some/newmodule': {
      'foo': 'foo1.2'
    },
    // When some/oldmodule requires foo it gets the older version.
    'some/oldmodule': {
      'foo': 'foo1.0'
    }
  }
});

Require ERRBACKS

  • Errbacks give you a chance to catch module loading failure and do something else.
  • Or fetching script errors! Huge win!

Require Errbacks - CDN failure

requirejs.config({
  paths: {
    jquery: 'http://code.jquery.com/jquery-1.8.2.js'
  }
});

require(['jquery'], function ($) {
  //Do something with $ here
}, function (err) {
  //The error has a list of modules that failed
  var failedId = err.requireModules && err.requireModules[0];
  if (failedId === 'jquery') {
    //undef is function only on the global requirejs object.
    //Use it to clear internal knowledge of jQuery. Any modules
    //that were dependent on jQuery and in the middle of loading
    //will not be loaded yet, they will wait until a valid jQuery
    //does load.
    requirejs.undef(failedId);

    //Set the path to jQuery to local path
    requirejs.config({
        paths: {
            jquery: 'local/jquery'
        }
    });

    //Try again. Note that the above require callback
    //with the "Do something with $ here" comment will
    //be called if this new attempt to load jQuery succeeds.
    require(['jquery'], function () {});
  } else {
    //Some other error. Maybe show message to the user.
  }
});

Require Errbacks - CDN failure 2

You can pass an array to load in order

requirejs.config({
  // To get timely, correct error triggers in IE, force
  // a define/shim exports check.
  enforceDefine: true,
  paths: {
    jquery: [
      'http://code.jquery.com/jquery-1.8.2.js',
      //If the CDN location fails, load from this location
      'lib/jquery'
    ]
  }
});

//Later
require(['jquery'], function ($) {
});

Graceful module shutdown - restart

require(['chat'], function(Chat){
  Chat.start();
}, function(err){
  console.log('Chat broke my heart and failed me.');
  var failedId = err.requireModules && err.requireModules[0];
  requirejs.undef(failedId);
  
  // Log an error, call a service telling your developers
  // to stop being n00bs.
  require(['log'], function(log){
    log.ourDevsAreScrubs();
  });

  // Maybe apologize to user through ui?
  // Finally I hope this is merciful... round 2

  console.log('Ok time to try it again.');
  require(['chat'], function(Chat){});

});

Main.js

requirejs.config({
  paths: {
    handlebars: 'vendor/handlebars-1.0.rc.1'
  },
  shim: {
    'handlebars': {
      exports: 'Handlebars'
    }
  }
});

require(['app/main'], function(app){
  app.start();
}, function(err) {
  console.log('Somekind of error. Time to restart.', err);
  // Restart
  require(['app/main'], function(app){
    app.start();
  });
});

Using Require plugins

  • Load different kinds of assets. Assets are dependencies too! (CSS, Templates, etc)
  • Can be used to preprocess module contents or load strategies. (Coffeescript, Non-AMD Scripts)
  • text!, css!, i18n!, cs!, json!, mdown!, jade!, & and more

Using Require plugins

define(['cs!module.coffee'], function(module){
  //module was compiled for me!
});

Using Require plugins

If you want to use templates but dont want to wrap them in script-tags or so, just use the text! plugin

define([
  'text!mytemplate.handlebars', 
  'handlebars'
], function(template, handlebars){
  // template is just a string
  return handlebars.compile(template);
});

Add text! plugin

requirejs.config({
  paths: {
    handlebars: 'vendor/handlebars-1.0.rc.1', 
    text: 'vendor/text'
  },
  shim: {
    'handlebars': {
      exports: 'Handlebars'
    }
  }
});

app/templates/app.handlebars

<h1>Hello {{name}}!</h1>

app/main.js

define([
  'handlebars',
  'app/libs/chaos',
  'text!templates/app.handlebars'
], function(Handlebars, chaos, text){
  var template = Handlebars.compile(text);
  //Return the Interface
  return {
    start: function() {
      document.body.innerHTML = template({name: 'Init'});
    }
  }
});

By using Require plugins

  • We can significantly reduce boilerplate, and further separate our concerns with plugins!
  • The plugins will write to file in the build, this means the whole application could be a single request.

r.js - builds & performance

A big win is that you can work in development without a extra build-step

  • CLI implemented with Node.js
  • Traces dependencies
  • Concatenates and uglify your modules for production
  • Will rename unnamed modules
  • Plugins are supported: (text! inlined for example)
  • Use a shim like Almond if you don't lazy load code.
  • Minify sub modules or the entire app
  • Precompile templates at build-time so your app doesn't need todo that a runtime

Thank you!