Brief History of
JavaScript
Modules
@sychoi
SungTheCoder
About me
- Web Developer
- Coder
- Programmer
- Software Engineer
~ 7 years of Web Development
Many many years ago
Demo App
Source Code
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
</body>
</html>
Source Code
JavaScript - Main
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document.getElementById("answer").innerHTML = answer;
Source Code
JavaScript - sum
function sum(arr){
return reduce(arr, add);
}
Source Code
JavaScript - add
function add(a, b) {
return a + b;
}
Source Code
JavaScript - reduce
function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
for(index += 1; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
}
main
sum
add
reduce
Inline Script
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script type="text/javascript">
function add(a, b) {
return a + b;
}
function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
for(index += 1; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
}
function sum(arr){
return reduce(arr, add);
}
/* Main Function */
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document.getElementById("answer").innerHTML = answer;
</script>
</body>
</html>
add
reduce
sum
main
- Lack of Code Reusability
- Pollution of global namespace
- Lack of Dependency Resolution
Script Tags
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script type="text/javascript" src="./add.js"></script>
<script type="text/javascript" src="./reduce.js"></script>
<script type="text/javascript" src="./sum.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
{
}
add.js
//add.js
function add(a, b) {
return a + b;
}
reduce.js
//reduce.js
function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
}
sum.js
//sum.js
function sum(arr){
return reduce(arr, add);
}
main.js
// main.js
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document
.getElementById("answer")
.innerHTML = answer;
Lack of Code Reusability- Pollution of global namespace
- Lack of Dependency Resolution
Module Object & IIFE
Module Pattern
my-app.js
//my-app.js
var myApp = {};
Exposes one object
IIFE
(function(){
/*... your code goes here ...*/
})();
Immediately-Invoked Function Expression
add.js
//add.js
(function(){
myApp.add = function(a, b) {
return a + b;
}
})();
reduce.js
(function(){
myApp.reduce = function(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
}
})();
sum.js
(function(){
myApp.sum = function(arr){
return myApp.reduce(arr, myApp.add);
}
})();
main.js
(function(app){
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = app.sum(values)
document
.getElementById("answer")
.innerHTML = answer;
})(myApp);
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script type="text/javascript" src="./my-app.js"></script>
<script type="text/javascript" src="./add.js"></script>
<script type="text/javascript" src="./reduce.js"></script>
<script type="text/javascript" src="./sum.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
{
}
- Lack of Code Reusability
Pollution of global namespace- Lack of Dependency Resolution
CommonJS
- Formerly ServerJS
- Standardization Organization
- Defines Common APIs for web servers, desktop and command line applications.
Export Syntax
//app.js
module.export = function add(a, b) {
return a + b;
};
Import Syntax
var add = require('./app.js');
- Synchronous Syntax
- Not suitable for browser application
AMD
Asynchronous Module Definition
CommonJS Module/Transfer/C format
Export/Import
define([‘add’, ‘reduce’],
function(add, reduce){
return function(){...};
}
);
RequireJS
JavaScript Module Loader
- Download 'require.js' from http://requirejs.org
- Add it to <script> tag
How to
main.js
// main.js
define(['sum'], function(sum){
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document
.getElementById("answer")
.innerHTML = answer;
})
No Return Statement
sum.js
// sum.js
define(['add', 'reduce'], function(add, reduce){
var sum = function(arr){
return reduce(arr, add);
};
return sum;
})
add.js
// add.js
define([], function(){
var add = function(a, b){
return a + b;
};
return add;
});
reduce.js
// reduce.js
define([], function(){
var reduce = function(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
}
return reduce;
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script data-main="main" src="require.js"></script>
</body>
</html>
Lack of Code ReusabilityPollution of global namespaceLack of Dependency Resolution
- AMD syntax too verbose
- List of dependencies in the array must match the list of arguments in the function.
- With current browsers(HTTP 1.1), loading many small files can degrade the performance.
Browserify
JavaScript Module Bundler
- Allow us to use CommonJS module syntax for browser application
- Module Bundler
- Traverse dependency tree of the code and bundle them up into a single file
Install Browserify
npm install -g browserify
main.js
//main.js
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document
.getElementById("answer")
.innerHTML = answer;
sum.js
//sum.js
var reduce = require('./reduce');
var add = require('./add');
module.exports = function(arr){
return reduce(arr, add);
};
add.js
//add.js
module.exports = function add(a,b){
return a + b;
};
reduce.js
//reduce.js
module.exports = function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
};
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script src="bundle.js"></script>
</body>
</html>
?
bundle.js?
$ brwoserify main.js -o bundle.js
bundle.js
function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function add(a,b){
return a + b;
};
},{}],2:[function(require,module,exports){
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document.getElementById("answer").innerHTML = answer;
},{"./sum":4}],3:[function(require,module,exports){
module.exports = function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
};
},{}],4:[function(require,module,exports){
var reduce = require('./reduce');
var add = require('./add');
module.exports = function(arr){
return reduce(arr, add);
};
},{"./add":1,"./reduce":3}]},{},[2]);
Building Library?
What Module type?
- Global Object (Module Pattern)
- CommonJS
- AMD
- All of the above
UMD
Universal Module Definition
sum.umd.js
//sum.umd.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['add', 'reduce'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('add'), require('reduce'));
} else {
// Browser globals (root is window)
root.sum = factory(root.add, root.reduce);
}
}(this, function (add, reduce) {
// private methods
// exposed public methods
return function(arr) {
return reduce(arr, add);
}
}));
Your Next Application?
- Global Object (Module Pattern)
- CommonJS
- AMD
- UMD
- None of above
ES6 Module
import
export
main.js
// main.js
import sum from "./sum";
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);
document
.getElementById("answer")
.innerHTML = answer;
sum.js
// sum.js
import add from './add';
import reduce from './reduce';
export default function sum(arr){
return reduce(arr, add);
}
add.js
// add.js
export default function add(a,b){
return a + b;
}
reduce.js
//reduce.js
export default function reduce(arr, iteratee) {
let index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index]);
}
return memo;
}
- Concise Syntax
- Future of JavaScript
Not Supported by all the browsers
Webpack
- Module Bundler
- Support CommonJS, AMD
- ES6 Module can be supported
- More features
- Code Split
- Loader
- Plugin
- Webpack DevServer
Webpack
Getting Started
$ mkdir project; cd project
$ npm init -y
$ npm install -D webpack webpack-dev-server
webpack.config.js
module.exports = {
entry: './app/main.js',
output: {
path: './dist',
filename: 'bundle.js'
}
}
package.json
:
:
"scripts": {
"start": "webpack-dev-server -progress -colors",
"build": "webpack"
},
:
:
app/main.js
// app/main.js
var sum = require('./sum');
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document
.getElementById("answer")
.innerHTML = answer;
app/sum.js
// app/sum.js
define(['./reduce', './add'],
function(reduce, add){
sum = function(arr){
return reduce(arr, add);
}
return sum;
}
);
app/add.js
// app/add.js
module.exports = function add(a,b){
return a + b;
};
app/reduce.js
// app/reduce.js
module.exports = function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
};
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script src="dist/bundle.js"></script>
</body>
</html>
$ npm start
$ ls dist/
$
$ npm build
$ ls dist/
bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var sum = __webpack_require__(1);
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values)
document.getElementById("answer").innerHTML = answer;
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(2), __webpack_require__(3)], __WEBPACK_AMD_DEFINE_RESULT__ = function(reduce, add){
sum = function(arr){
return reduce(arr, add);
}
return sum;
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ },
/* 2 */
/***/ function(module, exports) {
module.exports = function reduce(arr, iteratee) {
var index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index])
}
return memo;
};
/***/ },
/* 3 */
/***/ function(module, exports) {
module.exports = function add(a,b){
return a + b;
};
/***/ }
/******/ ]);
Rollup
Rollup
- Module Bundler
- Support CommonJS, AMD and ES6
- Tree Shaking
Getting Started
$ npm install -g rollup
main.js
// main.js
import sum from "./sum";
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);
document
.getElementById("answer")
.innerHTML = answer;
sum.js
// sum.js
import { add } from './math';
import reduce from './reduce';
export default (arr) => reduce(arr, add);
reduce.js
// reduce.js
export default (arr, iteratee) => {
let index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index]);
}
return memo;
}
math.js
let add = (a,b) => a + b;
let sub = (a,b) => a - b;
export { add, sub };
Build
$ rollup main.js -o bundle.js
let add = (a,b) => a + b;
var reduce = (arr, iteratee) => {
let index = 0,
length = arr.length,
memo = arr[index];
index += 1;
for(; index < length; index += 1){
memo = iteratee(memo, arr[index]);
}
return memo;
}
var sum = (arr) => reduce(arr, add);
var values = [ 1, 2, 4, 5, 6, 7, 8, 9 ];
var answer = sum(values);
document.getElementById("answer").innerHTML = answer;
SystemJS
SystemJS
- Module Loader
- Support CommonJS, AMD and ES6
- Build on top of ES6 module loader polyfill
- The syntax and API will most likely to be part of the language - Future proof
To Load a Module
<script src="system.js"></script>
<script>
System.import('/js/main.js')
.then(thing => {
/* do something with thing */
});
</script>
Asynchronously
JSPM
JSPM
- Package Manager
- Module Loader
- Module Bundler
- Support CommonJS, AMD and ES6
- Dev Server
Installing 3rd party modules
$ jspm install npm:package-name
$ jspm install github:package/name
will download the package from ‘npm’ or ‘github’ and install it on ‘jspm_packages’ directory
jspm-server
- Development Server
- Detects code change and reloads browser
- Make use of SystemJS module loader
bundler
$ jspm bundle main.js bundle.js
Under the hood, JSPM uses Rollup for bundling
PARCEL
- 🚀 Blazing fast bundle times - multicore compilation, and a filesystem cache for fast rebuilds even after a restart.
- 📦 Out of the box support for JS, CSS, HTML, file assets, and more - no plugins to install.
- 🐠 Automatically transforms modules using Babel, PostCSS, and PostHTML when needed - even node_modules.
- ✂️ Zero configuration code splitting using dynamic import() statements.
- 🔥 Built in support for hot module replacement
- 🚨 Friendly error logging experience - syntax highlighted code frames help pinpoint the problem.
Install parcel
npm install -g parcel-bundler
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script src="app/main.js"></script>
</body>
</html>
index.html
$ parcel index.html
Development Server
$ parcel build index.html
✨ Built in 274ms.
dist/main.1054a117.map 1.57 KB 6ms
dist/main.1054a117.js 1.99 KB 13ms
dist/index.html 201 B 5ms
$ ls -lh dist
-rw-r--r-- 1 sung staff 201B index.html
-rw-r--r-- 1 sung staff 2.0K main.1054a117.js
-rw-r--r-- 1 sung staff 1.6K main.1054a117.map
Production Build
$ cat dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JS Modules</title>
</head>
<body>
<h1>
The Answer is
<span id="answer"></span>
</h1>
<script src="/main.1054a117.js"></script>
</body>
</html>
dist/index.html
Source Maps
dist/main.1054a771.js
arcelRequire=function(e,r,n,t){var i="function"==typeof parcelRequire&&parcelRequire,o="function"==typeof require&&require;function u(n,t){if(!r[n]){if(!e[n]){var f="function"==typeof parcelRequire&&parcelRequire;if(!t&&f)return f(n,!0);if(i)return i(n,!0);if(o&&"string"==typeof n)return o(n);var c=new Error("Cannot find module '"+n+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[n][1][r]||r},p.cache={};var l=r[n]=new u.Module(n);e[n][0].call(l.exports, p,l,l.exports,this)}return r[n].exports;function p(e){return u(p.resolve(e))}}u.isParcelRequire=!0,u.Module=function(e){this.id=e,this.bundle=u,this.exports={}},u.modules=e,u.cache=r,u.parent=i,u.register=function(r,n){e[r]=[function(e,r){r.exports=n},{}]};for(var f=0;f<n.length;f++)u(n[f]);if(n.length){var c=u(n[n.length-1]);"object"==typeof exports&&"undefined"!=typeof module?module.exports=c:"function"==typeof define&&define.amd?define(function(){return c}):t&&(this[t]=c)}return u}({"OwWD":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.add=void 0;var e=function(e,r){return e+r};exports.add=e;
},{}],"y4BJ":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var e=function(e,t){var r=0,o=e.length,u=e[r];for(r+=1;r<o;r+=1)u=t(u,e[r]);return u};exports.default=e;
},{}],"IY9c":[function(require,module,exports) {
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var e=require("./add"),r=t(require("./reduce"));function t(e){return e&&e.__esModule?e:{default:e}}var u=function(t){return(0,r.default)(t,e.add)};exports.default=u;
},{"./add":"OwWD","./reduce":"y4BJ"}],"PFbx":[function(require,module,exports) {
"use strict";var e=r(require("./sum"));function r(e){return e&&e.__esModule?e:{default:e}}var t=[1,2,4,5,6,7,8,9],u=(0,e.default)(t);document.getElementById("answer").innerHTML=u;
},{"./sum":"IY9c"}]},{},["PFbx"], null)
//# sourceMappingURL=/main.1054a771.map
dist/main.1054a771.map
{
"version":3,
"sources":["app/add.js", "app/reduce.js", "app/sum.js", "app/main.js"],
"names":["add", "a", "b", "arr", "iteratee", "index", "length", "memo",
"values", "answer", "document", "getElementById", "innerHTML" ],
"file":"main.1054a771.map",
"sourceRoot":"..",
"mappings":";AAAA,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,SAAA,EAAA,IAAIA,EAAM,SAACC,EAAEC,GAAMD,OAAAA,EAAIC,GAAvB,QAAA,IAAA;;ACUC,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,aAAA,EAVc,IAAA,EAAA,SAACC,EAAKC,GACfC,IAAAA,EAAQ,EACZC,EAASH,EAAIG,OACbC,EAAOJ,EAAIE,GAGLA,IADNA,GAAS,EACHA,EAAQC,EAAQD,GAAS,EAC7BE,EAAOH,EAASG,EAAMJ,EAAIE,IAErBE,OAAAA,GACR,QAAA,QAAA;;ACPc,aAAA,OAAA,eAAA,QAAA,aAAA,CAAA,OAAA,IAAA,QAAA,aAAA,EAHf,IAAA,EAAA,QAAA,SACA,EAAA,EAAA,QAAA,aAEe,SAAA,EAAA,GAAA,OAAA,GAAA,EAAA,WAAA,EAAA,CAAA,QAAA,GAAA,IAAA,EAAA,SAACJ,GAAQ,OAAA,EAAOA,EAAAA,SAAAA,EAAKH,EAAZ,MAAT,QAAA,QAAA;;ACEf,aALA,IAAA,EAAA,EAAA,QAAA,UAKA,SAAA,EAAA,GAAA,OAAA,GAAA,EAAA,WAAA,EAAA,CAAA,QAAA,GAHA,IAAIQ,EAAS,CAAE,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAChCC,GAAS,EAAID,EAAAA,SAAAA,GAEjBE,SAASC,eAAe,UAAUC,UAAYH"
}
https://sourcemaps.info/spec.html
http://slides.com/sungthecoder/js-module-history
https://github.com/sungthecoder/js-modules-examples
Brief History of JS modules
By Sungyeol Choi
Brief History of JS modules
Are you new to JavaScript and confused about module, module loader and module bundler? Or you have been writing JavaScript code for a while, but can’t get a grip of the jargons about module? You heard about the jargons like CommonJS, AMD, Browserify, SystemJS, Webpack, JSPM, etc., but don’t understand why we need them? I will try to explain what are they, and what problem they try to solve, and how they solve the problem.
- 3,281