Perpetuum module – IIFE, CommonJS, AMD, ES6 – Why modules?



Perpetuum module – IIFE, CommonJS, AMD, ES6 – Why modules?

0 0


js-modules-talk


On Github kirilknysh / js-modules-talk

Perpetuum module

IIFE, CommonJS, AMD, ES6

by Kiril Knysh

Why modules?

  • Web sites become Web apps  => Complex architecture
  • Hard app assembly
  • Code / performance optimizations
Web sites become Web apps and as a result - more complex architecture

Why modules?

Real project file (written not by EPAM). Pay attention to lines number.

JS modules alternatives

  • IIFE (Immediately Invoking Function Expressions)
  • CommonJS (mainly server JS code)
  • AMD (Asynchronous Module Definition) (mainly browsers)
  • ECMAScript 6 modules
Most popular JS modules approaches.

IIFE

Single file module

utils.jsApp.UTILS = (function () {
  var utils = {};
  var STATE = {
    NAME_PREFIX: 'Sir ',
    SAL_PREFIX: '€'
  };
  
  utils.getName = function (actor) {
    return STATE.NAME_PREFIX + $.trim(actor.name);
  }
  utils.getSalary = function (actor) {
    return STATE.SAL_PREFIX + actor.salary;
  }
  
  return utils;
})();

IIFE

Multi files module

utils/name.js(function (utils, $) {
  var STATE = {
    PREFIX: 'Sir '
  };
  utils.getName = function (actor) {
    return STATE.PREFIX + $.trim(actor.name);
  }
})(App.UTILS = App.UTILS || {}, window.jQuery);
utils/salary.js(function (utils, $) {
  var STATE = {
    PREFIX: '€'
  };
  utils.getSalary = function (actor) {
    return STATE.PREFIX + actor.salary;
  }
})(App.UTILS = App.UTILS || {}, window.jQuery);

CommonJS

utils.jsvar $ = require('jQuery');

var STATE = {
  NAME_PREFIX: 'Sir ',
  SAL_PREFIX: '€'
};
  
function getName(actor) {
  return STATE.NAME_PREFIX + $.trim(actor.name);
}
function getSalary(actor) {
  return STATE.SAL_PREFIX + actor.salary;
}
  
module.exports.getName = getName;
module.exports.getSalary = getSalary;
app.jsvar UTILS = require('utils');
  
UTILS.getName(actor);

CommonJS Loaders

AMD

utils.jsdefine(['jquery'] , function ($) {
  var STATE = {
    NAME_PREFIX: 'Sir ',
    SAL_PREFIX: '€'
  };
  
  return {
    getName: function (actor) {
      return STATE.NAME_PREFIX + $.trim(actor.name);
    },
    getSalary: function (actor) {
      return STATE.SAL_PREFIX + actor.salary;
    }
  };
});
app.jsrequire(['utils'], function (UTILS) {
  UTILS.getName(actor);
});

AMD Loaders

AMD Loaders (RequireJS)

demo/requirejs
index.html
<body>
    ...
    <script data-main="main" src="bower_components/requirejs/require.js"></script>
</body>
main.jsrequirejs.config({
  paths: {
    jquery: 'bower_components/jquery/dist/jquery'
  }
});

requirejs(['scripts/app'], function(app) {
  app.run();
});

AMD Loaders (r.js optimizer)

demo/requirejs
build.js({
  baseUrl: ".",
  name: "main",
  out: "./build/main.js",
  paths: {
    jquery: "bower_components/jquery/dist/jquery.min"
  }
})

ECMAScript 6 modules

utils.jsimport $ from 'jquery';
  
const STATE = {
  NAME_PREFIX: 'Sir ',
  SAL_PREFIX: '€'
};
  
export function getName(actor) {
  return STATE.NAME_PREFIX + $.trim(actor.name);
}
export function getSalary(actor) {
  return STATE.SAL_PREFIX + actor.salary;
}
app.jsimport { getName, getSalary } from 'utils';
  
getName(actor);

ECMAScript 6 modules

Browsers support

Thank you for attention!

ES6 modules loaders

ES6 modules loaders (webpack)

demo/es6-webpack
webpack.config.jsvar path = require('path');

module.exports = {
  entry: './scripts/app.js',
  output: {
    path: __dirname,
    filename: 'app.bundle.js'
  },
  module: {
    loaders: [
      { test: path.join(__dirname, 'scripts'), loader: 'babel-loader' }
    ]
  }
};

ES6 modules

  • each module is a logically dedicated piece of code
  • by default all declarations are local (private) but some could be marked as exports
  • module can import other modules (or their parts)
  • modules are singletones

ES6 modules

exports

  • named
  • default (one per module)

ES6 modules

named exports

utils.jsexport const SEC_IN_MINUTE = 60;
export function getName(actor) {
  return actor.name || '';
}
export class Actor(name, salary) {
  this.name = name;
  this.salary = salary;
}
app.jsimport { SEC_IN_MINUTE, getName } from 'utils';
import * as UTILS from 'utils';
const actor = new UTILS.Actor('Will Smith', 2000000);

ES6 modules

default export

super-sort.jsexport default function (array, predicate) {
  //super sort algorithm here
} //no semicolon
Actor.jsexport default class (name, salary) {
  this.name = name;
  this.salary = salary;
} //no semicolon
app.jsimport superSort from 'super-sort';
import SuperActor from 'Actor';

ES6 modules

named + default export

Actor.jsexport default class (name, salary) {
  this.name = name;
  this.salary = salary;
}
export function getName(actor) {
  return actor.name || '';
}
app.jsimport Actor, { getName as getActorName } from 'Actor';

ES6 modules

inline vs clause export

inline.jsexport const SEC_IN_MINUTE = 60;
export function getName(actor) {
  return actor.name || '';
}
clause.jsconst SEC_IN_MINUTE = 60;
function getName(actor) {
  return actor.name || '';
}
 
export { SEC_IN_MINUTE, getName as getActorName };

ES6 modules

imports

  • import name from "module-name";
  • import * as name from "module-name";
  • import { member } from "module-name";
  • import { member1 , member2 as alias2 , [...] } from "module-name";
  • import defaultMember, { member [ , [...] ] } from "module-name";
  • import defaultMember, * as alias from "module-name";
  • import defaultMember from "module-name";
  • import "module-name";

ES6 modules

named imports

utils.jsexport function getSalary(actor) {
  return actor.salary || -1;
}
export default function(actor) {
  return actor.name || '';
}
app.jsimport getName from 'utils';
import * as actorUtils from 'utils';
import { getSalary as getActorSalary } from 'utils';

ES6 modules

default imports

utils.jsexport function getSalary(actor) {
  return actor.salary || -1;
}
export default function(actor) {
  return actor.name || '';
}
app.jsimport getName from 'utils';
import getName, * as actorUtils from 'utils';
import getName, { getSalary as getActorSalary } from 'utils';

ES6 modules

empty import

utils.jsexport function getSalary(actor) {
  return actor.salary || -1;
}
export default function(actor) {
  return actor.name || '';
}
app.jsimport 'utils';

ES6 modules

Imports are hoisted

Actor.jsexport default class (name, salary) {
  this.name = name;
  this.salary = salary;
}
app.jsconst mFreeman = new Actor('Morgan Freeman', 3000000);
import Actor from 'Actor';

ES6 modules

Imports are read-only views on exports

visit-utils.jsexport let visits = 0;
export function visitorDetected() {
  visits++;
}
app.jsimport { visits, visitorDetected } from 'visit-utils';
console.log(visits); //0
visitorDetected();
console.log(visits); //1

ES6 modules

cyclic dependencies

demo/es6-webpack

bad cyclic dependencies

demo/es6-webpack-unresolved

ES6 modules

module loader API

JavaScript Loader Standard

ES6 modules

module loader API

System.import('some_module')
  .then(some_module => {
    // Use some_module
  })
  .catch(error => {
    ···
  });

ES6 modules

module loader API

  • Convert source code to module
    System.module(source, options?)
  • Register module
    System.set(name, module)
  • Evaluate module code and register it
    System.define(name, source, options?)

ES6 modules

module loader demo

demo/es6-module-loader

ES6 modules

native

demo/es6-native

Questions?

Perpetuum module IIFE, CommonJS, AMD, ES6 by Kiril Knysh