By Gion Kunz
Why ECMAScript modules are the way to go
<!doctype html>
<title>Prepare to wait...</title>
<script src="script1.js"></script>
<script src="script2.js"></script>
<script src="script3.js"></script>
<script src="script4.js"></script>
<script src="script5.js"></script>
<script src="script6.js"></script>
<script src="script7.js"></script>
<script src="script8.js"></script>
<script src="script9.js"></script>
<script src="script10.js"></script>
<script src="script11.js"></script>
<script src="script12.js"></script>
<script src="script13.js"></script>
<script src="script14.js"></script>
<script src="script15.js"></script>
<script src="script16.js"></script>
(function(g){
// main.js
g.Package = g.Package || {};
// module-a.js
g.Package.ModuleA = {
sayHi: function() {
console.log(g.Package.ModuleB.message);
}
};
// module-b.js
g.Package.ModuleB = {
message: 'Hi there!'
};
// boot.js
g.Package.ModuleA.sayHi();
}(window));
(function(g) {
// module-a.js
g.moduleA = (function() {
// Private to module via closure
var message = 'Hi There!';
function sayHi() {
console.log(message);
}
// Public interface
return {
sayHi: sayHi
};
}());
// init.js
g.moduleA.sayHi();
}(window))
By Addy Osmani
import {doStuff} from 'B';
doStuff();
Module A
export function doStuff() {
// All sorts of stuff
}
Module B
class Util {
static doStuff() {
// You bet!
}
static doMoreStuff() {
// More stuff...
}
}
Util.doStuff();
Util.doMoreStuff();
export function doStuff() {
// You bet!
}
export function doMoreStuff() {
// More stuff...
}
Module Util
export class Counter {
constructor() {
return Counter._i ||
(Counter._i = Object.create(Counter.prototype),
Counter._i._count = 0,
Counter._i);
}
increment() {
this._count++;
}
count() {
return this._count;
}
}
new Counter().increment();
new Counter().increment();
console.log(new Counter().count());
let _count = 0;
export function increment() {
_count++;
}
export function count() {
return _count;
}
Asynchronous Module Definition (Require.js)
define('A', ['B'],
function(B) {
B.doSomething();
}
);
Module A
define('B', [],
function() {
return {
doSomething() {
// Something...
}
};
}
);
Module B
Inversion of Control
Modules execute when dependencies are registered
Concatenate using named definition
Quite verbose format
Can't handle cyclic dependencies
No syntax support
No partial / named exports
CommonJS Modules (Node.js)
const B = require('B');
B.doSomething();
Module A
function doSomething() {
// Something...
}
module.exports = {
doSomething
};
Module B
Simple format
Relatively small runtime
Supports default export using module.exports
Great popularity (Node.js)
No Inversion of Control
Manually handle cyclic dependencies
Synchronous import only
No syntax support
ECMAScript Module
import {doSomething} from 'B';
doSomething();
Module A
export function doSomething() {
// Something...
}
Module B
There's a spec!!!
Syntax support
Default exports
Named exports
Bindings
Static Analysis
Dynamic "import( )"
Tree-shaking
Needs transpiler
import {a} from 'const';
console.log(a);
export const a = 42;
export const b = 23;
export const c = 1.618;
Module Main
Module Const
var a = 42;
console.log(a);
Bundler
Loader
Webpack
Browserify
rollup.js
Require.js
SystemJS
ES Module Loader
Require.js
Webpack
Browserify
SystemJS
Specification for loading modules in the browser.
<!doctype html>
<script type="module"
src="module-a.js">
</script>
By Guy Bedford
<!DOCTYPE html>
<html>
<head>
<script src="./babel-browser-build.js"></script>
<script src="./browser-es-module-loader.js"></script>
</head>
<body>
<script type="module" src="./module-a.js"></script>
</body>
</html>
import {message} from 'module-b.js';
document.body.textContent = message;
module-a.js
export const message = 'Modules are awesome! :-)';
module-b.js
Universal dynamic module loader - loads ES6 modules, AMD, CommonJS and global scripts in the browser and NodeJS.
<!doctype html>
<html>
<head>
<title>SystemJS CJS Modules</title>
<script src="//unpkg.com/systemjs"></script>
</head>
<body>
<script>
System
.import('module-a.js')
.catch(console.error.bind(window));
</script>
</body>
</html>
var message = require('module-b.js').message;
document.body.innerText = message;
exports.message = 'Modules are awesome!';
module-a.js
module-b.js
<!doctype html>
<html>
<head>
<title>SystemJS path to NPMCDN</title>
<script src="//unpkg.com/systemjs"></script>
<script src="//unpkg.com/babel-core/lib/api/browser.js"></script>
</head>
<body>
<script>
System.config({
transpiler: 'babel',
paths: {
'npm:*': '//unpkg.com/*'
}
});
System
.import('main.js')
.catch(console.error.bind(window));
</script>
</body>
</html>
import {merge} from 'npm:lodash';
import formatJson from 'npm:json-markup';
const obj1 = {
name: 'Test',
props: {
prop1: 1,
prop2: 2
}
};
const obj2 = {
name: 'Test2',
props: {
prop2: 22,
prop3: 3
}
};
const merged = merge({}, obj1, obj2);
document.body.innerHTML = formatJson(merged);
main.js
exports.fetch = function(load) {
return fetch(load.address)
.then(function(response) {
return response.text();
});
};
exports.translate = function(load) {
return 'module.exports = \'' +
load.source
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/'/g, '\\\'')
+ '\'';
};
import content from 'content.html!text.js';
document.body.innerHTML = content;
main.js
text.js
export function fetch(data) {
return loadBinaryData(data.address)
.then((binaryData: IBinaryData) => {
if (this.builder) {
return `data:${binaryData.contentType};base64,${
btoa(new Uint8Array(binaryData.data)
.reduce((data, byte) =>
data + String.fromCharCode(byte), '')
)
}`;
} else {
return '';
}
});
}
export function translate(data) {
if (this.builder) {
return `
var image = document.createElement('img');
image.src = '${data.source}';
module.exports = {
address: '${data.address}',
loadedImage: new Promise(function(resolve) {
image.onload = resolve(image);
})
};
`;
} else {
return '';
}
}
export function instantiate(data) {
const loadedImage = new Promise((resolve) => {
const image = document.createElement('img');
image.src = data.address;
image.onload = () => resolve(image);
});
return Promise.resolve({
address: data.address,
loadedImage
});
}
export function fetch(data: any) {
return loadBinaryData(data.address)
.then((binaryData: IBinaryData) => {
if (this.builder) {
return btoa(
new Uint8Array(binaryData.data)
.reduce((data, byte) =>
data + String.fromCharCode(byte), '')
);
} else {
data.metadata.binaryData = binaryData;
return '';
}
});
}
export function translate(data) {
if (this.builder) {
return `
module.exports = {
url: '${data.address}',
buffer: Uint8Array.from(
atob('${data.source}'),
function(c) { return c.charCodeAt(0) }
).buffer
}
`;
} else {
return '';
}
}
export function instantiate(data) {
return {
url: data.address,
buffer: data.metadata.binaryData.data
};
}
JSPM (JavaScript Package Manager), is a Broker between Packages and SystemJS
Uses SystemJS Builder
Includes minimal runtime
Uses Rollup.js to do tree-shaking
Pokémon images & names © 1995-2017 Nintendo/Game Freak.
Animated background images http://giphy.com/