oddEVEN
We provide marketing technology, front end and software development consulting to bring your digital marketing and ecommerce projects to the next level.
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/
By oddEVEN
There are many ways to deal with modules today. This presentation aims to show why SystemJS is not just an other module loader / bundler.
We provide marketing technology, front end and software development consulting to bring your digital marketing and ecommerce projects to the next level.