SlideShare une entreprise Scribd logo
1  sur  201
Télécharger pour lire hors ligne
Unbundling the JavaScript
module bundler
"La magia dietro Webpack e altri module bundlers"
Luciano Mammino - @loige
loige.link/bundle-coderful
1
Webpack == PAIN!?
twitter.com/search?q=webpack%20pain
@loige 2
@loige 3
@loige 4
@loige 5
@loige
❤
💔 💔
6
@loige 7
@loige 8
@loige 9
It's not Webpack!
Module bundling is actually complicated!
@loige 10
👋 Hello, I am Luciano!
Principal Software Engineer at FabFitFun
 Blog:
 Twitter:
 GitHub:  
loige.co
@loige
@lmammino
nodejsdp.link/3rd
11
loige.link/bundle-coderful 12
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 13
https://poo.loige.co
@loige 14
App features
@loige 15
Dynamic DOM manipulation
React, Angular, Vue
are so... overrated! 😆
@loige 16
Dynamic Favicon
favico.js@loige 17
Custom animated tooltips
tippy.js@loige 18
🎉 Confetti rainfall 😱
dom-confetti@loige 19
Persist state through Local Storage + UUIDs
 +store2 uuidjs@loige 20
@loige 21
@loige 21
7 Requests only for the JS code!
@loige 21
Current scenario
zepto@1.2.0/dist/zepto.min.js
uuidjs@4.0.3/dist/uuid.core.js
store2@2.7.0/dist/store2.min.js
tippy.js@2.2.3/dist/tippy.all.min.js
confetti@0.0.10/lib/main.js
favico.js@0.3.10/favico-0.3.10.min.js
@loige 22
Ideal scenario
zepto@1.2.0/dist/zepto.min.js
uuidjs@4.0.3/dist/uuid.core.js
store2@2.7.0/dist/store2.min.js
tippy.js@2.2.3/dist/tippy.all.min.js
confetti@0.0.10/lib/main.js
favico.js@0.3.10/favico-0.3.10.min.js
vendors.js
+
+
+
+
+
=
@loige 23
How to do this?
@loige 24
./buildVendors.sh > vendors.js
buildVendors.sh
$
@loige 25
npx lumpy build$
github.com/lmammino/lumpy
@loige 26 . 1
# lumpy.txt
https://unpkg.com/zepto@1.2.0/dist/zepto.min.js
https://unpkg.com/uuidjs@4.0.3/dist/uuid.core.js
https://unpkg.com/store2@2.7.0/dist/store2.min.js
https://unpkg.com/tippy.js@2.2.3/dist/tippy.all.min.js
https://unpkg.com/confetti-js@0.0.11/dist/index.min.js
https://unpkg.com/dom-confetti@0.0.10/lib/main.js
https://unpkg.com/favico.js@0.3.10/favico-0.3.10.min.js
Lumpy allows you to define
all the vendors in a text file
@loige 26 . 2
lumpy build$
1. Downloads the files from lumpy.txt (and caches them)
2. Concatenates the content of the files
3. Minifies the resulting source code (using )
4. Saves the resulting content in vendors.js
babel-minify
@loige 26 . 3
7 requests
2 requests
@loige 27
7 requests
2 requests
@loige
Even better if you "minify" these!
27
Concatenation
+
Minification
@loige 28
This is good...
@loige 29
This is good...
@loige 29
This is good...
... in 2008
was
@loige 29
Today...
We can do better!
@loige 30
Updating them should be easy
We shouldn't worry about transitive dependencies
(dependencies of dependencies)
Order of imports shouldn't really matter
We rely on dependencies!
@loige 31
Dependency
(or coupling)
a state in which one object uses a function of
another object
— Wikipedia
@loige 32
Reusable dependencies...
Modules!
@loige 33
Modules
The bricks for structuring non-trivial applications,
but also the main mechanism to enforce information
hiding by keeping private all the functions and
variables that are not explicitly marked to be
exported
— *Node.js Design Patterns
* yeah, I quite like quoting my stuff... 😅@loige 34
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 35
Meet my friend I.I.F.E.
(Immediately Invoked Function Expression)
loige.link/iife
@loige 36
We generally define a function this way
@loige 37
We generally define a function this way
const sum = (a, b) => a + b
@loige 37
We generally define a function this way
const sum = (a, b) => a + b
or
@loige 37
We generally define a function this way
const sum = (a, b) => a + b
function sum(a, b) {
return a + b
}
or
@loige 37
We generally define a function this way
const sum = (a, b) => a + b
function sum(a, b) {
return a + b
}
or
then, at some point, we execute it...
@loige 37
We generally define a function this way
const sum = (a, b) => a + b
function sum(a, b) {
return a + b
}
or
then, at some point, we execute it...
const four = sum(2, 2)
@loige 37
A function in JS creates an isolated scope
@loige 38
A function in JS creates an isolated scope
(a, b) => {
const secretString = "Hello"
return a + b
}
console.log(secretString) // undefined
@loige 38
A function in JS creates an isolated scope
(a, b) => {
const secretString = "Hello"
return a + b
}
console.log(secretString) // undefined
secretString is not visible outside the function
@loige 38
IIFE allows you to define an isolated scope
that executes itself
(arg1, arg2) => {
// do stuff here
const iAmNotVisibleOutside = true
}
@loige 39
IIFE allows you to define an isolated scope
that executes itself
(arg1, arg2) => {
// do stuff here
const iAmNotVisibleOutside = true
}
A function with its own scope
@loige 39
)(someArg1, someArg2)
IIFE allows you to define an isolated scope
that executes itself
(arg1, arg2) => {
// do stuff here
const iAmNotVisibleOutside = true
}
(
@loige 39
)(someArg1, someArg2)
IIFE allows you to define an isolated scope
that executes itself
(arg1, arg2) => {
// do stuff here
const iAmNotVisibleOutside = true
}
(
This wrapper executes the function immediately and passes arguments from the
outer scope
@loige 39
IIFE is a recurring pattern in
JavaScript modules
@loige 40
Let's implement a module
that provides:
 
Information hiding
exported functionalities
@loige 41
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})()
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined @loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined @loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
IIFE
Creates an isolated scope
and executes it
@loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
information hiding
non-exported functionality
@loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
defines exported
functionalities
@loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
propagates the exports
to the outer scope (assigning it to myModule)
@loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
Can access exported
functionalities
@loige42
const myModule = (() => {
const privateFoo = () => { /* ... */ }
const privateBar = [ /* ... */ ]
const exported = {
publicFoo: () => { /* ... */ },
publicBar: [ /* ... */ ]
};
return exported
})() A module
myModule.publicFoo()
myModule.publicBar[0]
myModule.privateFoo // undefined
myModule.privateBar // undefined
privateFoo // undefined
privateBar // undefined
No visibility for the
non-exported ones
@loige42
We want modules to be
 reusable across different apps and
organisations...
...we need
A STANDARD MODULE format!@loige 43
Module system features
Must have
Simple syntax for import / export
Information hiding
Allows to define modules in separate files
Modules can import from other modules
(nested dependencies)
@loige 44 . 1
Module system features
Nice to have
Ability to import module subsets
Avoid naming collision
Asynchronous module loading
Seamless support for Browsers & Server-side
@loige 44 . 2
JavaScript module systems
globals
CommonJS (Node.js)
AMD (Require.js / Dojo)
UMD
ES2015 Modules (ESM)
Many others (SystemJS, ...)
@loige 45
Globals
var $, jQuery
$ = jQuery = (() => {
return { /* ... */ }
})()
// ... use $ or jQuery in the global scope
$.find('.button').remove()
@loige 46
Globals
👎 Might generate naming collisions
(e.g. $ overrides browser global variable)
 
👎 Modules needs to be "fully loaded" in the right order
 
👎 Cannot import parts of modules
@loige 47
// or import single functionality
const { concat } = require('./loDash')
concat([1], [2], [3])
// app.js
// import full module
const _ = require('./loDash')
_.concat([1], [2], [3])
// loDash.js
const loDash = {
/* ... */
}
module.exports = loDash
CommonJS
@loige 48
// or import single functionality
const { concat } = require('./loDash')
concat([1], [2], [3])
// app.js
// import full module
const _ = require('./loDash')
_.concat([1], [2], [3])
// loDash.js
const loDash = {
/* ... */
}
module.exports = loDash
CommonJS
module
@loige 48
// or import single functionality
const { concat } = require('./loDash')
concat([1], [2], [3])
// app.js
// import full module
const _ = require('./loDash')
_.concat([1], [2], [3])
// loDash.js
const loDash = {
/* ... */
}
module.exports = loDash
CommonJS
module
app using module
@loige 48
// or import single functionality
const { concat } = require('./loDash')
concat([1], [2], [3])
// app.js
// import full module
const _ = require('./loDash')
_.concat([1], [2], [3])
// loDash.js
const loDash = {
/* ... */
}
module.exports = loDash
CommonJS
module
app using module
@loige 48
// or import single functionality
const { concat } = require('./loDash')
concat([1], [2], [3])
// app.js
// import full module
const _ = require('./loDash')
_.concat([1], [2], [3])
// loDash.js
const loDash = {
/* ... */
}
module.exports = loDash
CommonJS
module
app using module
@loige 48
CommonJS
👍 No naming collisions
(imported modules can be renamed)
👍 Huge repository of modules through
 
👎 Synchronous import only
👎 Works natively on the server side only (Node.js)
NPM
@loige 49
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
module
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
module
module name
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
module
dependencies
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
module
factory function used to construct the
module,
receives the dependencies as arguments
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// jquery-1.9.0.js
define(
'jquery',
['sizzle', 'jqueryUI'],
function (sizzle, jqueryUI) {
// Returns the exported value
return function () {
// ...
}
}
)
module
exported value
@loige 50
AMD ( )
Asynchronous Module Definition
Require.js
// app.js
// define paths
requirejs.config({
baseUrl: 'js/lib',
paths: {
jquery: 'jquery-1.9.0'
}
})
define(['jquery'], function ($) {
// this is executed only when jquery
// and its deps are loaded
});
@loige 51
AMD ( )
Asynchronous Module Definition
Require.js
// app.js
// define paths
requirejs.config({
baseUrl: 'js/lib',
paths: {
jquery: 'jquery-1.9.0'
}
})
define(['jquery'], function ($) {
// this is executed only when jquery
// and its deps are loaded
});
app
@loige 51
AMD ( )
Asynchronous Module Definition
Require.js
// app.js
// define paths
requirejs.config({
baseUrl: 'js/lib',
paths: {
jquery: 'jquery-1.9.0'
}
})
define(['jquery'], function ($) {
// this is executed only when jquery
// and its deps are loaded
});
app
Require.js config
jquery will be loaded from
://<currentDomain>/js/lib/jquery-1.9.0.js
@loige 51
AMD ( )
Asynchronous Module Definition
Require.js
// app.js
// define paths
requirejs.config({
baseUrl: 'js/lib',
paths: {
jquery: 'jquery-1.9.0'
}
})
define(['jquery'], function ($) {
// this is executed only when jquery
// and its deps are loaded
});
app
app main function
Has jquery as dependency
@loige 51
AMD ( )
Asynchronous Module Definition
Require.js
👍 Asynchronous modules
👍 Works on Browsers and Server side
 
👎 Very verbose and convoluted syntax (my opinion™)
@loige 52
UMD
Universal Module Definition
A module definition that is compatible with
Global modules, CommonJS & AMD
 
👉  👈github.com/umdjs/umd
@loige 53 . 1
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('dep'))
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['dep'], function (dep) {
return (root.returnExportsGlobal = factory(dep))
})
} else {
// Global Variables
root.myModule = factory(root.dep)
}
}(this, function (dep) {
// Your actual module
return {}
}))
loige.link/umd@loige 53 . 2
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('dep'))
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['dep'], function (dep) {
return (root.returnExportsGlobal = factory(dep))
})
} else {
// Global Variables
root.myModule = factory(root.dep)
}
}(this, function (dep) {
// Your actual module
return {}
}))
loige.link/umd
IIFE with arguments:
 - Current scope (this) and the module
factory function.
 - "dep" is a sample dependency of the
module.
@loige 53 . 2
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('dep'))
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['dep'], function (dep) {
return (root.returnExportsGlobal = factory(dep))
})
} else {
// Global Variables
root.myModule = factory(root.dep)
}
}(this, function (dep) {
// Your actual module
return {}
}))
loige.link/umd@loige 53 . 2
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('dep'))
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['dep'], function (dep) {
return (root.returnExportsGlobal = factory(dep))
})
} else {
// Global Variables
root.myModule = factory(root.dep)
}
}(this, function (dep) {
// Your actual module
return {}
}))
loige.link/umd@loige 53 . 2
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('dep'))
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['dep'], function (dep) {
return (root.returnExportsGlobal = factory(dep))
})
} else {
// Global Variables
root.myModule = factory(root.dep)
}
}(this, function (dep) {
// Your actual module
return {}
}))
loige.link/umd@loige 53 . 2
👍 Allows you to define modules that
can be used by almost any module loader
 
👎 Complex, the wrapper code
is almost impossible to write manually
UMD
Universal Module Definition
@loige 53 . 3
ES2015 modules
Cool & broad subject, it would deserve it's own talk
Wanna know more?
🔗 /  syntax reference
🔗
🔗
🔗
import export
ECMAScript modules in browsers
ES modules: A cartoon deep-dive
ES Modules in Node Today!
@loige 54 . 1
// calculator.js
const add = (num1, num2) => num1 + num2
const sub = (num1, num2) => num1 - num2
const div = (num1, num2) => num1 / num2
const mul = (num1, num2) => num1 * num2
export { add, sub, div, mul }
// app.js
import { add } from './calculator'
console.log(add(2,2)) // 4
ES2015 modules
@loige 54 . 2
// calculator.js
const add = (num1, num2) => num1 + num2
const sub = (num1, num2) => num1 - num2
const div = (num1, num2) => num1 / num2
const mul = (num1, num2) => num1 * num2
export { add, sub, div, mul }
// app.js
import { add } from './calculator'
console.log(add(2,2)) // 4
ES2015 modules
module
@loige 54 . 2
// calculator.js
const add = (num1, num2) => num1 + num2
const sub = (num1, num2) => num1 - num2
const div = (num1, num2) => num1 / num2
const mul = (num1, num2) => num1 * num2
export { add, sub, div, mul }
// app.js
import { add } from './calculator'
console.log(add(2,2)) // 4
ES2015 modules
moduleexported functionalities
@loige 54 . 2
// calculator.js
const add = (num1, num2) => num1 + num2
const sub = (num1, num2) => num1 - num2
const div = (num1, num2) => num1 / num2
const mul = (num1, num2) => num1 * num2
export { add, sub, div, mul }
// app.js
import { add } from './calculator'
console.log(add(2,2)) // 4
ES2015 modules
module
app
@loige 54 . 2
// calculator.js
const add = (num1, num2) => num1 + num2
const sub = (num1, num2) => num1 - num2
const div = (num1, num2) => num1 / num2
const mul = (num1, num2) => num1 * num2
export { add, sub, div, mul }
// app.js
import { add } from './calculator'
console.log(add(2,2)) // 4
ES2015 modules
module
app
import specific functionality
@loige 54 . 2
// index.html
<html>
<body>
<!-- ... -->
<script type="module">
import { add } from 'calculator.js'
console.log(add(2,2)) // 4
</script>
</body>
</html>
ES2015 modules
@loige 54 . 3
// index.html
<html>
<body>
<!-- ... -->
<script type="module">
import { add } from 'calculator.js'
console.log(add(2,2)) // 4
</script>
</body>
</html>
ES2015 modules
"works" in some modern browsers
@loige 54 . 3
ES2015 modules
🙄 Syntactically very similar to CommonJS...
BUT
👍 import & export are static
(allow static analysis of dependencies)
 
👍 It is a (still work in progress) standard format
 
👍 Works (almost) seamlessly in browsers & servers
@loige 54 . 4
So many options...
Current most used practice:
Use CommonJS or ES2015 & create "compiled bundles"
@loige 55
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 56
Let's try to use CommonJS
in the browser
@loige 57
const $ = require('zepto')
const tippy = require('tippy.js')
const UUID = require('uuidjs')
const { confetti } = require('dom-confetti/src/main')
const store = require('store2')
const Favico = require('favico.js')
!(function () {
const colors = ['#a864fd', '#29cdff', '#78ff44', '#ff718d', '#fdff6a']
const todoApp = (rootEl, opt = {}) => {
const todos = opt.todos || []
let completedTasks = opt.completedTasks || 0
const onChange = opt.onChange || (() => {})
const list = rootEl.find('.todo-list')
const footer = rootEl.find('.footer')
const todoCount = footer.find('.todo-count')
const insertInput = rootEl.find('.add-todo-box input')
const insertBtn = rootEl.find('.add-todo-box button')
const render = () => {
let tips
list.html('')
The browser doesn't know how
to process require.
It doesn't support CommonJS!
@loige 58
Module Bundler
A tool that takes modules with dependencies and emits
static assets representing those modules
 
Those static assets can be processed by browsers!
@loige 59
Dependency graph
A graph built by connecting every module with its direct
dependencies.
app
dependency A dependency B
dependency A2
shared
dependency
@loige 60
A module bundler has to:
1. Construct the dependency graph
(Dependency Resolution)
2. Assemble the modules in the graph into a
single executable asset (Packing)
@loige 61
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
62@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
62
(entrypoint)
(1)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
62
(entrypoint)
(1)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
62
(entrypoint)
(1)
(2)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
parser
62
(entrypoint)
(1)
(2)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
parser
62
(entrypoint)
(1)
(2)
(3)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
parser resolver
62
(entrypoint)
(1)
(2)
(3)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator
parser resolver
62
(entrypoint)
(1)
(2)
(3)
(4)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator log
parser resolver
62
(entrypoint)
(1)
(2)
(3)
(4)
@loige
// app.js
const calculator = require('./calculator')
const log = require('./log')
log(calculator('2 + 2 / 4'))
// log.js
module.exports = console.log
// calculator.js
const parser = require('./parser')
const resolver = require('./resolver')
module.exports = (expr) => resolver(parser(expr))
// parser.js
module.exports = (expr) => { /* ... */ }
// resolver.js
module.exports = (tokens) => { /* ... */ }
Dependency resolution
app
calculator log
parser resolver
62
(entrypoint)
(1)
(2)
(3)
(4)
(5)
@loige
During dependency resolution,
the bundler creates a modules map
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./parser': (module, require) => { … },
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
require path
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
module factory function
@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
const parser = require('./parser');const resolver =
require('./resolver');module.exports = (expr) =>
resolver(parser(expr))@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
const parser = require('./parser');const resolver =
require('./resolver');module.exports = (expr) =>
resolver(parser(expr))@loige 63
During dependency resolution,
the bundler creates a modules map
{
}
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … },
const parser = require('./parser');const resolver =
require('./resolver');module.exports = (expr) =>
resolver(parser(expr))@loige 63
Packing
{
--- : ---
--- : ----
--- : --
}
Modules map
@loige 64
Packing
{
--- : ---
--- : ----
--- : --
}
Modules map
@loige 64
Packing
.js
{
--- : ---
--- : ----
--- : --
}
Modules map
Single executable
JS file
@loige 64
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
) 65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
IIFE passing the modules map as
argument
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
Custom require function:
it will load the modules by
evaluating the code from the
modules map
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
A reference to a module with an
empty module.exports. 
This will be filled at evaluation
time
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
Invoking the factory function for
the given module name.
(Service locator pattern)
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
The current reference module is
passed, the factory function will
modify this object by adding the
proper exported values.
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
The custom require function is
passed so, modules can
recursively require other modules
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
The resulting module.exports is
returned
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
)
The entrypoint module is
required, triggering the actual
execution of the business logic
65
@loige
Packed executable file
((modulesMap) => {
const require = (name) => {
const module = { exports: {} }
modulesMap[name](module, require)
return module.exports
}
require('./app')
})(
{
'./app': (module, require) => { … },
'./calculator': (module, require) => { … },
'./log': (module, require) => { … },
'./parser': (module, require) => { … },
'./resolver': (module, require) => { … }
}
) 65
@loige
Now you know how
Module bundlers work!
 
And how to convert code written
using CommonJS to a single file that
works in the browser
@loige 66
A challenge for you!
If you do, ... I'll arrange a prize for you!
 
TIP: you can use  or  to parse JavaScript files (look for
require and module.exports) and to map relative module paths to
actual files in the filesystem.
 
Need an inspiration?
Check the awesome  and !
let me know
acorn babel-parser
resolve
minipack @adamisnotdead's w_bp_ck
Can you build a (simple) module bundler from scratch?
@loige 67
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 68
A state of the art module
bundler for the web
@loige 69
npm install webpack webpack-cli
@loige 70
webpack app.js
yep, recent versions of Webpack work without config! 😎
@loige 71
webpack --mode=development app.js
Do not compress the code and add
annotations!
@loige 72
loige.link/sample-webpacked-file
@loige73
Webpack concepts
Entry point: the starting file for dependency resolution.
Output: the destination file (bundled file).
Loaders: algorithms to parse different file types and convert them
into executable javascript (e.g. babel, typescript, but also CSS,
images or other static assets)
Plugins: do extra things (e.g. generate a wrapping HTML or
analysis tools)
@loige 74
const { resolve, join } = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
entry: './app.js',
output: {
path: resolve(join(__dirname, 'build')),
filename: 'app.js'
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
},
plugins: [
new CompressionPlugin()
]
}
Webpack.config.js
@loige75
const { resolve, join } = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
entry: './app.js',
output: {
path: resolve(join(__dirname, 'build')),
filename: 'app.js'
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
},
plugins: [
new CompressionPlugin()
]
}
Webpack.config.js
Entrypoint
Build the dependency graph starting from ./app.js
@loige75
const { resolve, join } = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
entry: './app.js',
output: {
path: resolve(join(__dirname, 'build')),
filename: 'app.js'
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
},
plugins: [
new CompressionPlugin()
]
}
Webpack.config.js
Output
Save the resulting bundled file in ./build/app.js
@loige75
const { resolve, join } = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
entry: './app.js',
output: {
path: resolve(join(__dirname, 'build')),
filename: 'app.js'
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
},
plugins: [
new CompressionPlugin()
]
}
Webpack.config.js
Loaders
All the files matching "*.js" are processed with babel
and converted to ES5 Javascript
@loige75
const { resolve, join } = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
entry: './app.js',
output: {
path: resolve(join(__dirname, 'build')),
filename: 'app.js'
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
},
plugins: [
new CompressionPlugin()
]
}
Webpack.config.js
Plugins
Uses a plugin that generates a gzipped copy of every
emitted file.
@loige75
Everything is a module
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
</div>
);
}
}
export default App 76
Everything is a module
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
</div>
);
}
}
export default App 76
Everything is a module
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
</div>
);
}
}
export default App 76
Everything is a module
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
</div>
);
}
}
export default App 76
Webpack can load any type of file
As long as you can provide a "loader" that
tells how to convert the file into
something the browser understands.
This is how Webpack allows you to use Babel, TypeScript,
Clojure, Elm, Imba but also to load CSSs, Images and other
assets.
77
{
test: /.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', 78
{
test: /.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
// Defines how to load .css files (uses a pipeline of loaders)
78
{
test: /.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
// Defines how to load .css files (uses a pipeline of loaders)
// parses the file with post-css
78
{
test: /.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
// Defines how to load .css files (uses a pipeline of loaders)
// parses the file with post-css
// process @import and url()
// statements
78
{
test: /.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9',
// Defines how to load .css files (uses a pipeline of loaders)
// parses the file with post-css
// process @import and url()
// statements
// inject the resulting code with a <style> tag
78
...Webpack can do (a lot) more!
Dev Server
Tree shaking
Dependencies analytics
Source maps
Async require / module splitting
@loige 79
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 80
Bundle cache busting
Device CDN
Origin
Server
bundle.js
(original)
81
Bundle cache busting
Device CDN
Origin
Server
example.com
bundle.js
(original)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js
bundle.js
(original)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
bundle.js
(original)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
bundle.js
(original)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
bundle.js
(original)
bundle.js
(cache copy)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
bundle.js
(original)
bundle.js
(cache copy)
81
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
bundle.js
(original)
bundle.js
(cache copy)
81
bundle.js
(cache copy)
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
82
bundle.js
(original)
bundle.js
(cache copy)
bundle.js
(cache copy)
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
82
bundle.js
(original)
bundle.js
(cache copy)
bundle.js
(cache copy)
NEW VERSION
STALE!
Bundle cache busting
Device CDN
Origin
Server
example.com bundle.js bundle.js
82
bundle.js
(original)
bundle.js
(cache copy)
bundle.js
(cache copy)
NEW VERSIONSTALE!
Bundle cache busting
83
Bundle cache busting
(Manual) Solution 1
83
Bundle cache busting
(Manual) Solution 1
bundle.js?v=1
bundle.js?v=2
bundle.js?v=3
83
Bundle cache busting
(Manual) Solution 1
bundle.js?v=1
bundle.js?v=2
bundle.js?v=3
Doesn't play nice with some CDNs and Proxies
(they won't consider different query parameters to be different resources)
83
Bundle cache busting
84
Bundle cache busting
(Manual) Solution 2
84
Bundle cache busting
(Manual) Solution 2
bundle-v1.js
bundle-v2.js
bundle-v3.js
...
84
Bundle cache busting
(Manual) Solution 2
bundle-v1.js
bundle-v2.js
bundle-v3.js
...
Better, but still a lot of diligence and manual effort needed...
84
Bundle cache busting
85
Bundle cache busting
Webpack Solution
85
Bundle cache busting
Webpack Solution
output: {
filename: '[name].[contenthash].js'
}
85
Bundle cache busting
Webpack Solution
...
bundle.9f61f58dd1cc3bb82182.js
bundle.aacdf58ef1aa12382199.js
bundle.ed61f68defef3bb82221.js
output: {
filename: '[name].[contenthash].js'
}
85
Bundle cache busting
Webpack Solution
 +
 
Every new asset version will generate a new file
cache is automatically cleaned up on every release
(if content actually changed)
html-plugin will update the reference to the new file
contenthash webpack-html-plugin
86
1. Why we need modules
2. JavaScript module systems
3. How a module bundler works
4. Webpack in 2 minutes!
5. Advanced module bundling
Agenda
@loige 87
Module bundlers are your friends
Now you know how they work,
they are not (really) magic!
Start small and add more when needed
If you try to build your own, you'll learn a
lot more!
@loige 88
Webpack is not
the only possibility!
89
Grazie!
     Special thanks:
, , ,
(reviewers) and
 (inspirations: his and his
)
@Podgeypoos79 @andreaman87 @mariocasciaro
@eugenserbanescu
@MarijnJH amazing book
workshop on JS modules
@loige
Images by
Pug-Unicorn cover image by
Background cover image by
Streamline Emoji pack
 1smr1 from Pixabay
Gerd from Pixabay
loige.link/bundle-coderful
90

Contenu connexe

Tendances

Tendances (10)

Maintaining a dependency graph with weaver
Maintaining a dependency graph with weaverMaintaining a dependency graph with weaver
Maintaining a dependency graph with weaver
 
The Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaThe Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony Barcelona
 
掀起 Swift 的面紗
掀起 Swift 的面紗掀起 Swift 的面紗
掀起 Swift 的面紗
 
Dr Frankenfunctor and the Monadster
Dr Frankenfunctor and the MonadsterDr Frankenfunctor and the Monadster
Dr Frankenfunctor and the Monadster
 
Koin Quickstart
Koin QuickstartKoin Quickstart
Koin Quickstart
 
JavaScript in 2016
JavaScript in 2016JavaScript in 2016
JavaScript in 2016
 
Reactive Programming with JavaScript
Reactive Programming with JavaScriptReactive Programming with JavaScript
Reactive Programming with JavaScript
 
Objective-C for Beginners
Objective-C for BeginnersObjective-C for Beginners
Objective-C for Beginners
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Testable JavaScript: Application Architecture
Testable JavaScript:  Application ArchitectureTestable JavaScript:  Application Architecture
Testable JavaScript: Application Architecture
 

Similaire à Unbundling the JavaScript module bundler - Road to Coderful

The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_function
timotheeg
 
Navigating the wild seas of es6 modules
Navigating the wild seas of es6 modulesNavigating the wild seas of es6 modules
Navigating the wild seas of es6 modules
Gil Tayar
 
Python pune talk decorators
Python pune talk   decoratorsPython pune talk   decorators
Python pune talk decorators
Sid Saha
 
ErlHive Safe Erlang Reloaded
ErlHive Safe Erlang ReloadedErlHive Safe Erlang Reloaded
ErlHive Safe Erlang Reloaded
guestdee461
 
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Droidcon Berlin
 

Similaire à Unbundling the JavaScript module bundler - Road to Coderful (20)

Unbundling the JavaScript module bundler - Øredev 21 Nov 2018
Unbundling the JavaScript module bundler - Øredev 21 Nov 2018Unbundling the JavaScript module bundler - Øredev 21 Nov 2018
Unbundling the JavaScript module bundler - Øredev 21 Nov 2018
 
Unbundling the JavaScript module bundler - Codemotion Rome 2018
Unbundling the JavaScript module bundler - Codemotion Rome 2018Unbundling the JavaScript module bundler - Codemotion Rome 2018
Unbundling the JavaScript module bundler - Codemotion Rome 2018
 
Unbundling the JavaScript module bundler - Luciano Mammino - Codemotion Rome ...
Unbundling the JavaScript module bundler - Luciano Mammino - Codemotion Rome ...Unbundling the JavaScript module bundler - Luciano Mammino - Codemotion Rome ...
Unbundling the JavaScript module bundler - Luciano Mammino - Codemotion Rome ...
 
Creational pattern 2
Creational pattern 2Creational pattern 2
Creational pattern 2
 
The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_function
 
Fpga 13-task-and-functions
Fpga 13-task-and-functionsFpga 13-task-and-functions
Fpga 13-task-and-functions
 
Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture project
 
Python import mechanism
Python import mechanismPython import mechanism
Python import mechanism
 
Navigating the wild seas of es6 modules
Navigating the wild seas of es6 modulesNavigating the wild seas of es6 modules
Navigating the wild seas of es6 modules
 
Sharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFSharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SF
 
Advanced JavaScript
Advanced JavaScriptAdvanced JavaScript
Advanced JavaScript
 
Pyramid Framework
Pyramid FrameworkPyramid Framework
Pyramid Framework
 
Python pune talk decorators
Python pune talk   decoratorsPython pune talk   decorators
Python pune talk decorators
 
ErlHive Safe Erlang Reloaded
ErlHive Safe Erlang ReloadedErlHive Safe Erlang Reloaded
ErlHive Safe Erlang Reloaded
 
Gephi Toolkit Tutorial
Gephi Toolkit TutorialGephi Toolkit Tutorial
Gephi Toolkit Tutorial
 
Construire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradleConstruire une application JavaFX 8 avec gradle
Construire une application JavaFX 8 avec gradle
 
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDKEric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
 
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
Droidcon2013 pro guard, optimizer and obfuscator in the android sdk_eric lafo...
 
Модули в С++ 20: хорошие, плохие и ужасные. Владислав Чехарев. CoreHard Sprin...
Модули в С++ 20: хорошие, плохие и ужасные. Владислав Чехарев. CoreHard Sprin...Модули в С++ 20: хорошие, плохие и ужасные. Владислав Чехарев. CoreHard Sprin...
Модули в С++ 20: хорошие, плохие и ужасные. Владислав Чехарев. CoreHard Sprin...
 
Di code steps
Di code stepsDi code steps
Di code steps
 

Plus de Luciano Mammino

Plus de Luciano Mammino (20)

Did you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJSDid you know JavaScript has iterators? DublinJS
Did you know JavaScript has iterators? DublinJS
 
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
What I learned by solving 50 Advent of Code challenges in Rust - RustNation U...
 
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS MilanoBuilding an invite-only microsite with Next.js & Airtable - ReactJS Milano
Building an invite-only microsite with Next.js & Airtable - ReactJS Milano
 
From Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiperFrom Node.js to Design Patterns - BuildPiper
From Node.js to Design Patterns - BuildPiper
 
Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!Let's build a 0-cost invite-only website with Next.js and Airtable!
Let's build a 0-cost invite-only website with Next.js and Airtable!
 
Everything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLsEverything I know about S3 pre-signed URLs
Everything I know about S3 pre-signed URLs
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
Serverless for High Performance Computing
Serverless for High Performance ComputingServerless for High Performance Computing
Serverless for High Performance Computing
 
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022JavaScript Iteration Protocols - Workshop NodeConf EU 2022
JavaScript Iteration Protocols - Workshop NodeConf EU 2022
 
Building an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & AirtableBuilding an invite-only microsite with Next.js & Airtable
Building an invite-only microsite with Next.js & Airtable
 
Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀Let's take the monolith to the cloud 🚀
Let's take the monolith to the cloud 🚀
 
A look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust DublinA look inside the European Covid Green Certificate - Rust Dublin
A look inside the European Covid Green Certificate - Rust Dublin
 
Monoliths to the cloud!
Monoliths to the cloud!Monoliths to the cloud!
Monoliths to the cloud!
 
The senior dev
The senior devThe senior dev
The senior dev
 
Node.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community VijayawadaNode.js: scalability tips - Azure Dev Community Vijayawada
Node.js: scalability tips - Azure Dev Community Vijayawada
 
A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)A look inside the European Covid Green Certificate (Codemotion 2021)
A look inside the European Covid Green Certificate (Codemotion 2021)
 
AWS Observability Made Simple
AWS Observability Made SimpleAWS Observability Made Simple
AWS Observability Made Simple
 
Semplificare l'observability per progetti Serverless
Semplificare l'observability per progetti ServerlessSemplificare l'observability per progetti Serverless
Semplificare l'observability per progetti Serverless
 
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
Finding a lost song with Node.js and async iterators - NodeConf Remote 2021
 
Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021Finding a lost song with Node.js and async iterators - EnterJS 2021
Finding a lost song with Node.js and async iterators - EnterJS 2021
 

Dernier

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 

Dernier (20)

The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 

Unbundling the JavaScript module bundler - Road to Coderful

  • 1. Unbundling the JavaScript module bundler "La magia dietro Webpack e altri module bundlers" Luciano Mammino - @loige loige.link/bundle-coderful 1
  • 10. It's not Webpack! Module bundling is actually complicated! @loige 10
  • 11. 👋 Hello, I am Luciano! Principal Software Engineer at FabFitFun  Blog:  Twitter:  GitHub:   loige.co @loige @lmammino nodejsdp.link/3rd 11
  • 13. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 13
  • 16. Dynamic DOM manipulation React, Angular, Vue are so... overrated! 😆 @loige 16
  • 19. 🎉 Confetti rainfall 😱 dom-confetti@loige 19
  • 20. Persist state through Local Storage + UUIDs  +store2 uuidjs@loige 20
  • 23. 7 Requests only for the JS code! @loige 21
  • 26. How to do this? @loige 24
  • 30. lumpy build$ 1. Downloads the files from lumpy.txt (and caches them) 2. Concatenates the content of the files 3. Minifies the resulting source code (using ) 4. Saves the resulting content in vendors.js babel-minify @loige 26 . 3
  • 32. 7 requests 2 requests @loige Even better if you "minify" these! 27
  • 36. This is good... ... in 2008 was @loige 29
  • 37. Today... We can do better! @loige 30
  • 38. Updating them should be easy We shouldn't worry about transitive dependencies (dependencies of dependencies) Order of imports shouldn't really matter We rely on dependencies! @loige 31
  • 39. Dependency (or coupling) a state in which one object uses a function of another object — Wikipedia @loige 32
  • 41. Modules The bricks for structuring non-trivial applications, but also the main mechanism to enforce information hiding by keeping private all the functions and variables that are not explicitly marked to be exported — *Node.js Design Patterns * yeah, I quite like quoting my stuff... 😅@loige 34
  • 42. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 35
  • 43. Meet my friend I.I.F.E. (Immediately Invoked Function Expression) loige.link/iife @loige 36
  • 44. We generally define a function this way @loige 37
  • 45. We generally define a function this way const sum = (a, b) => a + b @loige 37
  • 46. We generally define a function this way const sum = (a, b) => a + b or @loige 37
  • 47. We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or @loige 37
  • 48. We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or then, at some point, we execute it... @loige 37
  • 49. We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or then, at some point, we execute it... const four = sum(2, 2) @loige 37
  • 50. A function in JS creates an isolated scope @loige 38
  • 51. A function in JS creates an isolated scope (a, b) => { const secretString = "Hello" return a + b } console.log(secretString) // undefined @loige 38
  • 52. A function in JS creates an isolated scope (a, b) => { const secretString = "Hello" return a + b } console.log(secretString) // undefined secretString is not visible outside the function @loige 38
  • 53. IIFE allows you to define an isolated scope that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } @loige 39
  • 54. IIFE allows you to define an isolated scope that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } A function with its own scope @loige 39
  • 55. )(someArg1, someArg2) IIFE allows you to define an isolated scope that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } ( @loige 39
  • 56. )(someArg1, someArg2) IIFE allows you to define an isolated scope that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } ( This wrapper executes the function immediately and passes arguments from the outer scope @loige 39
  • 57. IIFE is a recurring pattern in JavaScript modules @loige 40
  • 58. Let's implement a module that provides:   Information hiding exported functionalities @loige 41
  • 59. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined @loige42
  • 60. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined @loige42
  • 61. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined IIFE Creates an isolated scope and executes it @loige42
  • 62. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined information hiding non-exported functionality @loige42
  • 63. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined defines exported functionalities @loige42
  • 64. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined propagates the exports to the outer scope (assigning it to myModule) @loige42
  • 65. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined Can access exported functionalities @loige42
  • 66. const myModule = (() => { const privateFoo = () => { /* ... */ } const privateBar = [ /* ... */ ] const exported = { publicFoo: () => { /* ... */ }, publicBar: [ /* ... */ ] }; return exported })() A module myModule.publicFoo() myModule.publicBar[0] myModule.privateFoo // undefined myModule.privateBar // undefined privateFoo // undefined privateBar // undefined No visibility for the non-exported ones @loige42
  • 67. We want modules to be  reusable across different apps and organisations... ...we need A STANDARD MODULE format!@loige 43
  • 68. Module system features Must have Simple syntax for import / export Information hiding Allows to define modules in separate files Modules can import from other modules (nested dependencies) @loige 44 . 1
  • 69. Module system features Nice to have Ability to import module subsets Avoid naming collision Asynchronous module loading Seamless support for Browsers & Server-side @loige 44 . 2
  • 70. JavaScript module systems globals CommonJS (Node.js) AMD (Require.js / Dojo) UMD ES2015 Modules (ESM) Many others (SystemJS, ...) @loige 45
  • 71. Globals var $, jQuery $ = jQuery = (() => { return { /* ... */ } })() // ... use $ or jQuery in the global scope $.find('.button').remove() @loige 46
  • 72. Globals 👎 Might generate naming collisions (e.g. $ overrides browser global variable)   👎 Modules needs to be "fully loaded" in the right order   👎 Cannot import parts of modules @loige 47
  • 73. // or import single functionality const { concat } = require('./loDash') concat([1], [2], [3]) // app.js // import full module const _ = require('./loDash') _.concat([1], [2], [3]) // loDash.js const loDash = { /* ... */ } module.exports = loDash CommonJS @loige 48
  • 74. // or import single functionality const { concat } = require('./loDash') concat([1], [2], [3]) // app.js // import full module const _ = require('./loDash') _.concat([1], [2], [3]) // loDash.js const loDash = { /* ... */ } module.exports = loDash CommonJS module @loige 48
  • 75. // or import single functionality const { concat } = require('./loDash') concat([1], [2], [3]) // app.js // import full module const _ = require('./loDash') _.concat([1], [2], [3]) // loDash.js const loDash = { /* ... */ } module.exports = loDash CommonJS module app using module @loige 48
  • 76. // or import single functionality const { concat } = require('./loDash') concat([1], [2], [3]) // app.js // import full module const _ = require('./loDash') _.concat([1], [2], [3]) // loDash.js const loDash = { /* ... */ } module.exports = loDash CommonJS module app using module @loige 48
  • 77. // or import single functionality const { concat } = require('./loDash') concat([1], [2], [3]) // app.js // import full module const _ = require('./loDash') _.concat([1], [2], [3]) // loDash.js const loDash = { /* ... */ } module.exports = loDash CommonJS module app using module @loige 48
  • 78. CommonJS 👍 No naming collisions (imported modules can be renamed) 👍 Huge repository of modules through   👎 Synchronous import only 👎 Works natively on the server side only (Node.js) NPM @loige 49
  • 79. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) @loige 50
  • 80. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module @loige 50
  • 81. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module module name @loige 50
  • 82. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module dependencies @loige 50
  • 83. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module factory function used to construct the module, receives the dependencies as arguments @loige 50
  • 84. AMD ( ) Asynchronous Module Definition Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module exported value @loige 50
  • 85. AMD ( ) Asynchronous Module Definition Require.js // app.js // define paths requirejs.config({ baseUrl: 'js/lib', paths: { jquery: 'jquery-1.9.0' } }) define(['jquery'], function ($) { // this is executed only when jquery // and its deps are loaded }); @loige 51
  • 86. AMD ( ) Asynchronous Module Definition Require.js // app.js // define paths requirejs.config({ baseUrl: 'js/lib', paths: { jquery: 'jquery-1.9.0' } }) define(['jquery'], function ($) { // this is executed only when jquery // and its deps are loaded }); app @loige 51
  • 87. AMD ( ) Asynchronous Module Definition Require.js // app.js // define paths requirejs.config({ baseUrl: 'js/lib', paths: { jquery: 'jquery-1.9.0' } }) define(['jquery'], function ($) { // this is executed only when jquery // and its deps are loaded }); app Require.js config jquery will be loaded from ://<currentDomain>/js/lib/jquery-1.9.0.js @loige 51
  • 88. AMD ( ) Asynchronous Module Definition Require.js // app.js // define paths requirejs.config({ baseUrl: 'js/lib', paths: { jquery: 'jquery-1.9.0' } }) define(['jquery'], function ($) { // this is executed only when jquery // and its deps are loaded }); app app main function Has jquery as dependency @loige 51
  • 89. AMD ( ) Asynchronous Module Definition Require.js 👍 Asynchronous modules 👍 Works on Browsers and Server side   👎 Very verbose and convoluted syntax (my opinion™) @loige 52
  • 90. UMD Universal Module Definition A module definition that is compatible with Global modules, CommonJS & AMD   👉  👈github.com/umdjs/umd @loige 53 . 1
  • 91. (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('dep')) } else if (typeof define === 'function' && define.amd) { // AMD define(['dep'], function (dep) { return (root.returnExportsGlobal = factory(dep)) }) } else { // Global Variables root.myModule = factory(root.dep) } }(this, function (dep) { // Your actual module return {} })) loige.link/umd@loige 53 . 2
  • 92. (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('dep')) } else if (typeof define === 'function' && define.amd) { // AMD define(['dep'], function (dep) { return (root.returnExportsGlobal = factory(dep)) }) } else { // Global Variables root.myModule = factory(root.dep) } }(this, function (dep) { // Your actual module return {} })) loige.link/umd IIFE with arguments:  - Current scope (this) and the module factory function.  - "dep" is a sample dependency of the module. @loige 53 . 2
  • 93. (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('dep')) } else if (typeof define === 'function' && define.amd) { // AMD define(['dep'], function (dep) { return (root.returnExportsGlobal = factory(dep)) }) } else { // Global Variables root.myModule = factory(root.dep) } }(this, function (dep) { // Your actual module return {} })) loige.link/umd@loige 53 . 2
  • 94. (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('dep')) } else if (typeof define === 'function' && define.amd) { // AMD define(['dep'], function (dep) { return (root.returnExportsGlobal = factory(dep)) }) } else { // Global Variables root.myModule = factory(root.dep) } }(this, function (dep) { // Your actual module return {} })) loige.link/umd@loige 53 . 2
  • 95. (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('dep')) } else if (typeof define === 'function' && define.amd) { // AMD define(['dep'], function (dep) { return (root.returnExportsGlobal = factory(dep)) }) } else { // Global Variables root.myModule = factory(root.dep) } }(this, function (dep) { // Your actual module return {} })) loige.link/umd@loige 53 . 2
  • 96. 👍 Allows you to define modules that can be used by almost any module loader   👎 Complex, the wrapper code is almost impossible to write manually UMD Universal Module Definition @loige 53 . 3
  • 97. ES2015 modules Cool & broad subject, it would deserve it's own talk Wanna know more? 🔗 /  syntax reference 🔗 🔗 🔗 import export ECMAScript modules in browsers ES modules: A cartoon deep-dive ES Modules in Node Today! @loige 54 . 1
  • 98. // calculator.js const add = (num1, num2) => num1 + num2 const sub = (num1, num2) => num1 - num2 const div = (num1, num2) => num1 / num2 const mul = (num1, num2) => num1 * num2 export { add, sub, div, mul } // app.js import { add } from './calculator' console.log(add(2,2)) // 4 ES2015 modules @loige 54 . 2
  • 99. // calculator.js const add = (num1, num2) => num1 + num2 const sub = (num1, num2) => num1 - num2 const div = (num1, num2) => num1 / num2 const mul = (num1, num2) => num1 * num2 export { add, sub, div, mul } // app.js import { add } from './calculator' console.log(add(2,2)) // 4 ES2015 modules module @loige 54 . 2
  • 100. // calculator.js const add = (num1, num2) => num1 + num2 const sub = (num1, num2) => num1 - num2 const div = (num1, num2) => num1 / num2 const mul = (num1, num2) => num1 * num2 export { add, sub, div, mul } // app.js import { add } from './calculator' console.log(add(2,2)) // 4 ES2015 modules moduleexported functionalities @loige 54 . 2
  • 101. // calculator.js const add = (num1, num2) => num1 + num2 const sub = (num1, num2) => num1 - num2 const div = (num1, num2) => num1 / num2 const mul = (num1, num2) => num1 * num2 export { add, sub, div, mul } // app.js import { add } from './calculator' console.log(add(2,2)) // 4 ES2015 modules module app @loige 54 . 2
  • 102. // calculator.js const add = (num1, num2) => num1 + num2 const sub = (num1, num2) => num1 - num2 const div = (num1, num2) => num1 / num2 const mul = (num1, num2) => num1 * num2 export { add, sub, div, mul } // app.js import { add } from './calculator' console.log(add(2,2)) // 4 ES2015 modules module app import specific functionality @loige 54 . 2
  • 103. // index.html <html> <body> <!-- ... --> <script type="module"> import { add } from 'calculator.js' console.log(add(2,2)) // 4 </script> </body> </html> ES2015 modules @loige 54 . 3
  • 104. // index.html <html> <body> <!-- ... --> <script type="module"> import { add } from 'calculator.js' console.log(add(2,2)) // 4 </script> </body> </html> ES2015 modules "works" in some modern browsers @loige 54 . 3
  • 105. ES2015 modules 🙄 Syntactically very similar to CommonJS... BUT 👍 import & export are static (allow static analysis of dependencies)   👍 It is a (still work in progress) standard format   👍 Works (almost) seamlessly in browsers & servers @loige 54 . 4
  • 106. So many options... Current most used practice: Use CommonJS or ES2015 & create "compiled bundles" @loige 55
  • 107. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 56
  • 108. Let's try to use CommonJS in the browser @loige 57
  • 109. const $ = require('zepto') const tippy = require('tippy.js') const UUID = require('uuidjs') const { confetti } = require('dom-confetti/src/main') const store = require('store2') const Favico = require('favico.js') !(function () { const colors = ['#a864fd', '#29cdff', '#78ff44', '#ff718d', '#fdff6a'] const todoApp = (rootEl, opt = {}) => { const todos = opt.todos || [] let completedTasks = opt.completedTasks || 0 const onChange = opt.onChange || (() => {}) const list = rootEl.find('.todo-list') const footer = rootEl.find('.footer') const todoCount = footer.find('.todo-count') const insertInput = rootEl.find('.add-todo-box input') const insertBtn = rootEl.find('.add-todo-box button') const render = () => { let tips list.html('') The browser doesn't know how to process require. It doesn't support CommonJS! @loige 58
  • 110. Module Bundler A tool that takes modules with dependencies and emits static assets representing those modules   Those static assets can be processed by browsers! @loige 59
  • 111. Dependency graph A graph built by connecting every module with its direct dependencies. app dependency A dependency B dependency A2 shared dependency @loige 60
  • 112. A module bundler has to: 1. Construct the dependency graph (Dependency Resolution) 2. Assemble the modules in the graph into a single executable asset (Packing) @loige 61
  • 113. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution 62@loige
  • 114. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app 62 (entrypoint) (1) @loige
  • 115. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator 62 (entrypoint) (1) @loige
  • 116. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator 62 (entrypoint) (1) (2) @loige
  • 117. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator parser 62 (entrypoint) (1) (2) @loige
  • 118. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator parser 62 (entrypoint) (1) (2) (3) @loige
  • 119. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator parser resolver 62 (entrypoint) (1) (2) (3) @loige
  • 120. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator parser resolver 62 (entrypoint) (1) (2) (3) (4) @loige
  • 121. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator log parser resolver 62 (entrypoint) (1) (2) (3) (4) @loige
  • 122. // app.js const calculator = require('./calculator') const log = require('./log') log(calculator('2 + 2 / 4')) // log.js module.exports = console.log // calculator.js const parser = require('./parser') const resolver = require('./resolver') module.exports = (expr) => resolver(parser(expr)) // parser.js module.exports = (expr) => { /* ... */ } // resolver.js module.exports = (tokens) => { /* ... */ } Dependency resolution app calculator log parser resolver 62 (entrypoint) (1) (2) (3) (4) (5) @loige
  • 123. During dependency resolution, the bundler creates a modules map @loige 63
  • 124. During dependency resolution, the bundler creates a modules map { } @loige 63
  • 125. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, @loige 63
  • 126. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, @loige 63
  • 127. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './parser': (module, require) => { … }, @loige 63
  • 128. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, @loige 63
  • 129. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, @loige 63
  • 130. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, require path @loige 63
  • 131. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, module factory function @loige 63
  • 132. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, const parser = require('./parser');const resolver = require('./resolver');module.exports = (expr) => resolver(parser(expr))@loige 63
  • 133. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, const parser = require('./parser');const resolver = require('./resolver');module.exports = (expr) => resolver(parser(expr))@loige 63
  • 134. During dependency resolution, the bundler creates a modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, const parser = require('./parser');const resolver = require('./resolver');module.exports = (expr) => resolver(parser(expr))@loige 63
  • 135. Packing { --- : --- --- : ---- --- : -- } Modules map @loige 64
  • 136. Packing { --- : --- --- : ---- --- : -- } Modules map @loige 64
  • 137. Packing .js { --- : --- --- : ---- --- : -- } Modules map Single executable JS file @loige 64
  • 138. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) 65 @loige
  • 139. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) IIFE passing the modules map as argument 65 @loige
  • 140. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) Custom require function: it will load the modules by evaluating the code from the modules map 65 @loige
  • 141. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) A reference to a module with an empty module.exports.  This will be filled at evaluation time 65 @loige
  • 142. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) Invoking the factory function for the given module name. (Service locator pattern) 65 @loige
  • 143. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) The current reference module is passed, the factory function will modify this object by adding the proper exported values. 65 @loige
  • 144. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) The custom require function is passed so, modules can recursively require other modules 65 @loige
  • 145. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) The resulting module.exports is returned 65 @loige
  • 146. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) The entrypoint module is required, triggering the actual execution of the business logic 65 @loige
  • 147. Packed executable file ((modulesMap) => { const require = (name) => { const module = { exports: {} } modulesMap[name](module, require) return module.exports } require('./app') })( { './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … } } ) 65 @loige
  • 148. Now you know how Module bundlers work!   And how to convert code written using CommonJS to a single file that works in the browser @loige 66
  • 149. A challenge for you! If you do, ... I'll arrange a prize for you!   TIP: you can use  or  to parse JavaScript files (look for require and module.exports) and to map relative module paths to actual files in the filesystem.   Need an inspiration? Check the awesome  and ! let me know acorn babel-parser resolve minipack @adamisnotdead's w_bp_ck Can you build a (simple) module bundler from scratch? @loige 67
  • 150. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 68
  • 151. A state of the art module bundler for the web @loige 69
  • 152. npm install webpack webpack-cli @loige 70
  • 153. webpack app.js yep, recent versions of Webpack work without config! 😎 @loige 71
  • 154. webpack --mode=development app.js Do not compress the code and add annotations! @loige 72
  • 156. Webpack concepts Entry point: the starting file for dependency resolution. Output: the destination file (bundled file). Loaders: algorithms to parse different file types and convert them into executable javascript (e.g. babel, typescript, but also CSS, images or other static assets) Plugins: do extra things (e.g. generate a wrapping HTML or analysis tools) @loige 74
  • 157. const { resolve, join } = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { entry: './app.js', output: { path: resolve(join(__dirname, 'build')), filename: 'app.js' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env'] ] } } } ] }, plugins: [ new CompressionPlugin() ] } Webpack.config.js @loige75
  • 158. const { resolve, join } = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { entry: './app.js', output: { path: resolve(join(__dirname, 'build')), filename: 'app.js' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env'] ] } } } ] }, plugins: [ new CompressionPlugin() ] } Webpack.config.js Entrypoint Build the dependency graph starting from ./app.js @loige75
  • 159. const { resolve, join } = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { entry: './app.js', output: { path: resolve(join(__dirname, 'build')), filename: 'app.js' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env'] ] } } } ] }, plugins: [ new CompressionPlugin() ] } Webpack.config.js Output Save the resulting bundled file in ./build/app.js @loige75
  • 160. const { resolve, join } = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { entry: './app.js', output: { path: resolve(join(__dirname, 'build')), filename: 'app.js' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env'] ] } } } ] }, plugins: [ new CompressionPlugin() ] } Webpack.config.js Loaders All the files matching "*.js" are processed with babel and converted to ES5 Javascript @loige75
  • 161. const { resolve, join } = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { entry: './app.js', output: { path: resolve(join(__dirname, 'build')), filename: 'app.js' }, module: { rules: [ { test: /.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ ['@babel/preset-env'] ] } } } ] }, plugins: [ new CompressionPlugin() ] } Webpack.config.js Plugins Uses a plugin that generates a gzipped copy of every emitted file. @loige75
  • 162. Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> </div> ); } } export default App 76
  • 163. Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> </div> ); } } export default App 76
  • 164. Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> </div> ); } } export default App 76
  • 165. Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> </div> ); } } export default App 76
  • 166. Webpack can load any type of file As long as you can provide a "loader" that tells how to convert the file into something the browser understands. This is how Webpack allows you to use Babel, TypeScript, Clojure, Elm, Imba but also to load CSSs, Images and other assets. 77
  • 167. { test: /.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', 78
  • 168. { test: /.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // Defines how to load .css files (uses a pipeline of loaders) 78
  • 169. { test: /.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // Defines how to load .css files (uses a pipeline of loaders) // parses the file with post-css 78
  • 170. { test: /.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // Defines how to load .css files (uses a pipeline of loaders) // parses the file with post-css // process @import and url() // statements 78
  • 171. { test: /.css$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // Defines how to load .css files (uses a pipeline of loaders) // parses the file with post-css // process @import and url() // statements // inject the resulting code with a <style> tag 78
  • 172. ...Webpack can do (a lot) more! Dev Server Tree shaking Dependencies analytics Source maps Async require / module splitting @loige 79
  • 173. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 80
  • 174. Bundle cache busting Device CDN Origin Server bundle.js (original) 81
  • 175. Bundle cache busting Device CDN Origin Server example.com bundle.js (original) 81
  • 176. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js (original) 81
  • 177. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) 81
  • 178. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) 81
  • 179. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) bundle.js (cache copy) 81
  • 180. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) bundle.js (cache copy) 81
  • 181. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) bundle.js (cache copy) 81 bundle.js (cache copy)
  • 182. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js 82 bundle.js (original) bundle.js (cache copy) bundle.js (cache copy)
  • 183. Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js 82 bundle.js (original) bundle.js (cache copy) bundle.js (cache copy) NEW VERSION
  • 184. STALE! Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js 82 bundle.js (original) bundle.js (cache copy) bundle.js (cache copy) NEW VERSIONSTALE!
  • 187. Bundle cache busting (Manual) Solution 1 bundle.js?v=1 bundle.js?v=2 bundle.js?v=3 83
  • 188. Bundle cache busting (Manual) Solution 1 bundle.js?v=1 bundle.js?v=2 bundle.js?v=3 Doesn't play nice with some CDNs and Proxies (they won't consider different query parameters to be different resources) 83
  • 191. Bundle cache busting (Manual) Solution 2 bundle-v1.js bundle-v2.js bundle-v3.js ... 84
  • 192. Bundle cache busting (Manual) Solution 2 bundle-v1.js bundle-v2.js bundle-v3.js ... Better, but still a lot of diligence and manual effort needed... 84
  • 195. Bundle cache busting Webpack Solution output: { filename: '[name].[contenthash].js' } 85
  • 196. Bundle cache busting Webpack Solution ... bundle.9f61f58dd1cc3bb82182.js bundle.aacdf58ef1aa12382199.js bundle.ed61f68defef3bb82221.js output: { filename: '[name].[contenthash].js' } 85
  • 197. Bundle cache busting Webpack Solution  +   Every new asset version will generate a new file cache is automatically cleaned up on every release (if content actually changed) html-plugin will update the reference to the new file contenthash webpack-html-plugin 86
  • 198. 1. Why we need modules 2. JavaScript module systems 3. How a module bundler works 4. Webpack in 2 minutes! 5. Advanced module bundling Agenda @loige 87
  • 199. Module bundlers are your friends Now you know how they work, they are not (really) magic! Start small and add more when needed If you try to build your own, you'll learn a lot more! @loige 88
  • 200. Webpack is not the only possibility! 89
  • 201. Grazie!      Special thanks: , , , (reviewers) and  (inspirations: his and his ) @Podgeypoos79 @andreaman87 @mariocasciaro @eugenserbanescu @MarijnJH amazing book workshop on JS modules @loige Images by Pug-Unicorn cover image by Background cover image by Streamline Emoji pack  1smr1 from Pixabay Gerd from Pixabay loige.link/bundle-coderful 90