Slide 1

Slide 1 text

Unbundling the JavaScript Unbundling the JavaScript module bundler module bundler Luciano Mammino - Luciano Mammino - @loige @loige 10/06/2019 loige.link/bundle-devit 1

Slide 2

Slide 2 text

Webpack == PAIN!? Webpack == PAIN!? twitter.com/search?q=webpack%20pain @loige 2

Slide 3

Slide 3 text

@loige 3

Slide 4

Slide 4 text

@loige 4

Slide 5

Slide 5 text

@loige 5

Slide 6

Slide 6 text

@loige ❤ 6

Slide 7

Slide 7 text

@loige 7

Slide 8

Slide 8 text

@loige 8

Slide 9

Slide 9 text

@loige 9

Slide 10

Slide 10 text

It's not Webpack! It's not Webpack! Module bundling is actually complicated! Module bundling is actually complicated! @loige 10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

Hello, I am Luciano! Hello, I am Luciano! 11

Slide 13

Slide 13 text

Hello, I am Luciano! Hello, I am Luciano! 11

Slide 14

Slide 14 text

Hello, I am Luciano! Hello, I am Luciano! 11

Slide 15

Slide 15 text

Hello, I am Luciano! Hello, I am Luciano! 11

Slide 16

Slide 16 text

Hello, I am Luciano! Hello, I am Luciano! Cloud Architect 11

Slide 17

Slide 17 text

Hello, I am Luciano! Hello, I am Luciano! Cloud Architect Blog: Twitter: GitHub: loige.co @loige @lmammino 11

Slide 18

Slide 18 text

Hello, I am Luciano! Hello, I am Luciano! Cloud Architect Blog: Twitter: GitHub: loige.co @loige @lmammino 11

Slide 19

Slide 19 text

loige.link/bundle-devit 12

Slide 20

Slide 20 text

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 Agenda @loige 13

Slide 21

Slide 21 text

https://poo.loige.co @loige 14

Slide 22

Slide 22 text

App features App features @loige 15

Slide 23

Slide 23 text

Dynamic DOM manipulation React, Angular, Vue are so... overrated! @loige 16

Slide 24

Slide 24 text

Dynamic Favicon favico.js @loige 17

Slide 25

Slide 25 text

Custom animated tooltips tippy.js @loige 18

Slide 26

Slide 26 text

Confetti rainfall dom-confetti @loige 19

Slide 27

Slide 27 text

Persist state through Local Storage + UUIDs + store2 uuidjs @loige 20

Slide 28

Slide 28 text

@loige 21

Slide 29

Slide 29 text

@loige 21

Slide 30

Slide 30 text

7 Requests only for the JS code! @loige 21

Slide 31

Slide 31 text

Current scenario Current scenario [email protected]/dist/zepto.min.js [email protected]/dist/uuid.core.js [email protected]/dist/store2.min.js [email protected]/dist/tippy.all.min.js [email protected]/lib/main.js [email protected]/favico­0.3.10.min.js @loige 22

Slide 32

Slide 32 text

Ideal scenario Ideal scenario [email protected]/dist/zepto.min.js [email protected]/dist/uuid.core.js [email protected]/dist/store2.min.js [email protected]/dist/tippy.all.min.js [email protected]/lib/main.js [email protected]/favico­0.3.10.min.js vendors.js + + + + + = @loige 23

Slide 33

Slide 33 text

How to do this? How to do this? @loige 24

Slide 34

Slide 34 text

./buildVendors.sh > vendors.js buildVendors.sh $ @loige 25

Slide 35

Slide 35 text

npx lumpy build $ github.com/lmammino/lumpy @loige 26 . 1

Slide 36

Slide 36 text

# lumpy.txt https://unpkg.com/[email protected]/dist/zepto.min.js https://unpkg.com/[email protected]/dist/uuid.core.js https://unpkg.com/[email protected]/dist/store2.min.js https://unpkg.com/[email protected]/dist/tippy.all.min.js https://unpkg.com/confetti­[email protected]/dist/index.min.js https://unpkg.com/dom­[email protected]/lib/main.js https://unpkg.com/[email protected]/favico­0.3.10.min.js Lumpy allows you to define all the vendors in a text file @loige 26 . 2

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

7 requests 2 requests @loige 27

Slide 39

Slide 39 text

7 requests 2 requests @loige Even better if you "minify" these! 27

Slide 40

Slide 40 text

Concatenation Concatenation + + Minification Minification @loige 28

Slide 41

Slide 41 text

This is good... This is good... @loige 29

Slide 42

Slide 42 text

This is good... This is good... @loige 29

Slide 43

Slide 43 text

This is good... This is good... ... in 2008 ... in 2008 was was @loige 29

Slide 44

Slide 44 text

Today... Today... We can do better! We can do better! @loige 30

Slide 45

Slide 45 text

Updating Updating them should be easy them should be easy We shouldn't worry about We shouldn't worry about transitive dependencies transitive dependencies (dependencies of dependencies) (dependencies of dependencies) Order of imports Order of imports shouldn't really matter shouldn't really matter We rely on We rely on dependencies dependencies! ! @loige 31

Slide 46

Slide 46 text

Dependency Dependency (or coupling) a state in which one object uses a function of another object — Wikipedia @loige 32

Slide 47

Slide 47 text

Reusable dependencies... Reusable dependencies... Modules! Modules! @loige 33

Slide 48

Slide 48 text

Modules 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 (Second Edition) * yeah, I quite like quoting my stuff... @loige 34

Slide 49

Slide 49 text

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 Agenda @loige 35

Slide 50

Slide 50 text

Meet my friend Meet my friend I.I.F.E. I.I.F.E. (Immediately Invoked Function Expression) loige.link/iife @loige 36

Slide 51

Slide 51 text

We generally define a function this way We generally define a function this way @loige 37

Slide 52

Slide 52 text

We generally define a function this way We generally define a function this way const sum = (a, b) => a + b @loige 37

Slide 53

Slide 53 text

We generally define a function this way We generally define a function this way const sum = (a, b) => a + b or or @loige 37

Slide 54

Slide 54 text

We generally define a function this way We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or or @loige 37

Slide 55

Slide 55 text

We generally define a function this way We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or or then, at some point, we execute it... then, at some point, we execute it... @loige 37

Slide 56

Slide 56 text

We generally define a function this way We generally define a function this way const sum = (a, b) => a + b function sum(a, b) { return a + b } or or then, at some point, we execute it... then, at some point, we execute it... const four = sum(2, 2) @loige 37

Slide 57

Slide 57 text

A function in JS creates an isolated scope A function in JS creates an isolated scope @loige 38

Slide 58

Slide 58 text

A function in JS creates an isolated scope A function in JS creates an isolated scope (a, b) => { const secretString = "Hello" return a + b } console.log(secretString) // undefined @loige 38

Slide 59

Slide 59 text

A function in JS creates an isolated scope A function in JS creates an isolated scope (a, b) => { const secretString = "Hello" return a + b } console.log(secretString) // undefined secretString secretString is not visible outside the function is not visible outside the function @loige 38

Slide 60

Slide 60 text

IIFE allows you to define an isolated scope IIFE allows you to define an isolated scope that executes itself that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } @loige 39

Slide 61

Slide 61 text

IIFE allows you to define an isolated scope IIFE allows you to define an isolated scope that executes itself that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } A function with its own scope @loige 39

Slide 62

Slide 62 text

)(someArg1, someArg2) IIFE allows you to define an isolated scope IIFE allows you to define an isolated scope that executes itself that executes itself (arg1, arg2) => { // do stuff here const iAmNotVisibleOutside = true } ( @loige 39

Slide 63

Slide 63 text

)(someArg1, someArg2) IIFE allows you to define an isolated scope IIFE allows you to define an isolated scope that executes itself 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

Slide 64

Slide 64 text

IIFE is a recurring pattern in IIFE is a recurring pattern in JavaScript modules JavaScript modules @loige 40

Slide 65

Slide 65 text

Let's implement a module Let's implement a module that provides: that provides: Information hiding Information hiding exported functionalities exported functionalities @loige 41

Slide 66

Slide 66 text

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 @loige 42

Slide 67

Slide 67 text

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 @loige 42

Slide 68

Slide 68 text

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 @loige 42

Slide 69

Slide 69 text

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 @loige 42

Slide 70

Slide 70 text

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 @loige 42

Slide 71

Slide 71 text

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) @loige 42

Slide 72

Slide 72 text

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 @loige 42

Slide 73

Slide 73 text

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 @loige 42

Slide 74

Slide 74 text

We want modules to be We want modules to be reusable reusable across different apps and across different apps and organisations... organisations... ...we need ...we need A A STANDARD MODULE STANDARD MODULE format! format! @loige 43

Slide 75

Slide 75 text

Module system features 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

Slide 76

Slide 76 text

Module system features 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

Slide 77

Slide 77 text

JavaScript module systems JavaScript module systems globals CommonJS (Node.js) AMD (Require.js / Dojo) UMD ES2015 Modules (ESM) Many others (SystemJS, ...) @loige 45

Slide 78

Slide 78 text

Globals Globals var $, jQuery $ = jQuery = (() => { return { /* ... */ } })() // ... use $ or jQuery in the global scope $.find('.button').remove() @loige 46

Slide 79

Slide 79 text

Globals 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

Slide 80

Slide 80 text

// 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 CommonJS @loige 48

Slide 81

Slide 81 text

// 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 CommonJS module @loige 48

Slide 82

Slide 82 text

// 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 CommonJS module app using module @loige 48

Slide 83

Slide 83 text

// 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 CommonJS module app using module @loige 48

Slide 84

Slide 84 text

// 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 CommonJS module app using module @loige 48

Slide 85

Slide 85 text

CommonJS 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

Slide 86

Slide 86 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) @loige 50

Slide 87

Slide 87 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module @loige 50

Slide 88

Slide 88 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 89

Slide 89 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js Require.js // jquery-1.9.0.js define( 'jquery', ['sizzle', 'jqueryUI'], function (sizzle, jqueryUI) { // Returns the exported value return function () { // ... } } ) module dependencies @loige 50

Slide 90

Slide 90 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 91

Slide 91 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 92

Slide 92 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 93

Slide 93 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 94

Slide 94 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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 :///js/lib/jquery-1.9.0.js @loige 51

Slide 95

Slide 95 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js 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

Slide 96

Slide 96 text

AMD ( AMD ( ) ) Asynchronous Module Definition Asynchronous Module Definition Require.js Require.js Asynchronous modules Works on Browsers and Server side Very verbose and convoluted syntax (my opinion™) @loige 52

Slide 97

Slide 97 text

UMD UMD Universal Module Definition Universal Module Definition A module definition that is compatible with Global modules, CommonJS & AMD github.com/umdjs/umd @loige 53 . 1

Slide 98

Slide 98 text

(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

Slide 99

Slide 99 text

(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

Slide 100

Slide 100 text

(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

Slide 101

Slide 101 text

(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

Slide 102

Slide 102 text

(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

Slide 103

Slide 103 text

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 UMD Universal Module Definition Universal Module Definition @loige 53 . 3

Slide 104

Slide 104 text

ES2015 modules 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

Slide 105

Slide 105 text

// 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 ES2015 modules @loige 54 . 2

Slide 106

Slide 106 text

// 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 ES2015 modules module @loige 54 . 2

Slide 107

Slide 107 text

// 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 ES2015 modules module exported functionalities @loige 54 . 2

Slide 108

Slide 108 text

// 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 ES2015 modules module app @loige 54 . 2

Slide 109

Slide 109 text

// 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 ES2015 modules module app import specific functionality @loige 54 . 2

Slide 110

Slide 110 text

// index.html import { add } from 'calculator.js' console.log(add(2,2)) // 4 ES2015 modules ES2015 modules @loige 54 . 3

Slide 111

Slide 111 text

// index.html import { add } from 'calculator.js' console.log(add(2,2)) // 4 ES2015 modules ES2015 modules "works" in some modern browsers @loige 54 . 3

Slide 112

Slide 112 text

ES2015 modules 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

Slide 113

Slide 113 text

So many options... So many options... Current most used practice: Use CommonJS or ES2015 & create "compiled bundles" @loige 55

Slide 114

Slide 114 text

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 Agenda @loige 56

Slide 115

Slide 115 text

Let's try to use CommonJS Let's try to use CommonJS in the browser in the browser @loige 57

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

Module Bundler 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

Slide 118

Slide 118 text

Dependency graph Dependency graph A graph built by connecting every module with its direct dependencies. app dependency A dependency B dependency A2 shared dependency @loige 60

Slide 119

Slide 119 text

A module bundler has to: 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

Slide 120

Slide 120 text

// 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 Dependency resolution 62 @loige

Slide 121

Slide 121 text

// 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 Dependency resolution app 62 (entrypoint) (1) @loige

Slide 122

Slide 122 text

// 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 Dependency resolution app calculator 62 (entrypoint) (1) @loige

Slide 123

Slide 123 text

// 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 Dependency resolution app calculator 62 (entrypoint) (1) (2) @loige

Slide 124

Slide 124 text

// 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 Dependency resolution app calculator parser 62 (entrypoint) (1) (2) @loige

Slide 125

Slide 125 text

// 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 Dependency resolution app calculator parser 62 (entrypoint) (1) (2) (3) @loige

Slide 126

Slide 126 text

// 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 Dependency resolution app calculator parser resolver 62 (entrypoint) (1) (2) (3) @loige

Slide 127

Slide 127 text

// 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 Dependency resolution app calculator parser resolver 62 (entrypoint) (1) (2) (3) (4) @loige

Slide 128

Slide 128 text

// 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 Dependency resolution app calculator log parser resolver 62 (entrypoint) (1) (2) (3) (4) @loige

Slide 129

Slide 129 text

// 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 Dependency resolution app calculator log parser resolver 62 (entrypoint) (1) (2) (3) (4) (5) @loige

Slide 130

Slide 130 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map @loige 63

Slide 131

Slide 131 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } @loige 63

Slide 132

Slide 132 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, @loige 63

Slide 133

Slide 133 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, @loige 63

Slide 134

Slide 134 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './parser': (module, require) => { … }, @loige 63

Slide 135

Slide 135 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, @loige 63

Slide 136

Slide 136 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, @loige 63

Slide 137

Slide 137 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, require path @loige 63

Slide 138

Slide 138 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map modules map { } './app': (module, require) => { … }, './calculator': (module, require) => { … }, './log': (module, require) => { … }, './parser': (module, require) => { … }, './resolver': (module, require) => { … }, module factory function @loige 63

Slide 139

Slide 139 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map 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

Slide 140

Slide 140 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map 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

Slide 141

Slide 141 text

During During dependency resolution dependency resolution, , the bundler creates a the bundler creates a modules map 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

Slide 142

Slide 142 text

Packing Packing { ­­­ : ­­­ ­­­ : ­­­­ ­­­ : ­­ } Modules map Modules map @loige 64

Slide 143

Slide 143 text

Packing Packing { ­­­ : ­­­ ­­­ : ­­­­ ­­­ : ­­ } Modules map Modules map @loige 64

Slide 144

Slide 144 text

Packing Packing .js { ­­­ : ­­­ ­­­ : ­­­­ ­­­ : ­­ } Modules map Modules map Single executable Single executable JS file JS file @loige 64

Slide 145

Slide 145 text

Packed executable file 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

Slide 146

Slide 146 text

Packed executable file 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

Slide 147

Slide 147 text

Packed executable file 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

Slide 148

Slide 148 text

Packed executable file 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

Slide 149

Slide 149 text

Packed executable file 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

Slide 150

Slide 150 text

Packed executable file 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

Slide 151

Slide 151 text

Packed executable file 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

Slide 152

Slide 152 text

Packed executable file 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

Slide 153

Slide 153 text

Packed executable file 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

Slide 154

Slide 154 text

Packed executable file 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

Slide 155

Slide 155 text

Now you know how Now you know how Module bundlers work! Module bundlers work! And how to convert code written And how to convert code written using using CommonJS CommonJS to a single file that to a single file that works in the browser works in the browser @loige 66

Slide 156

Slide 156 text

A challenge for you! 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? Can you build a (simple) module bundler from scratch? @loige 67

Slide 157

Slide 157 text

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 Agenda @loige 68

Slide 158

Slide 158 text

A state of the art module A state of the art module bundler for the web bundler for the web @loige 69

Slide 159

Slide 159 text

npm install webpack webpack­cli @loige 70

Slide 160

Slide 160 text

webpack app.js yep, recent versions of Webpack work without config! @loige 71

Slide 161

Slide 161 text

webpack ­­mode=development app.js Do not compress the code and add annotations! @loige 72

Slide 162

Slide 162 text

loige.link/sample-webpacked-file @loige 73

Slide 163

Slide 163 text

Webpack concepts 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

Slide 164

Slide 164 text

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 @loige 75

Slide 165

Slide 165 text

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 @loige 75

Slide 166

Slide 166 text

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 @loige 75

Slide 167

Slide 167 text

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 @loige 75

Slide 168

Slide 168 text

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. @loige 75

Slide 169

Slide 169 text

Everything is a module Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return (
logo

Welcome to React

); } } export default App 76

Slide 170

Slide 170 text

Everything is a module Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return (
logo

Welcome to React

); } } export default App 76

Slide 171

Slide 171 text

Everything is a module Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return (
logo

Welcome to React

); } } export default App 76

Slide 172

Slide 172 text

Everything is a module Everything is a module import React, { Component } from 'react' import logo from './logo.svg' import './App.css' class App extends Component { render() { return (
logo

Welcome to React

); } } export default App 76

Slide 173

Slide 173 text

Webpack can load any type of file Webpack can load any type of file As long as you can provide a " As long as you can provide a "loader loader" that " that tells how to convert the file into tells how to convert the file into something the browser understands. 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

Slide 174

Slide 174 text

{ 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

Slide 175

Slide 175 text

{ 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

Slide 176

Slide 176 text

{ 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

Slide 177

Slide 177 text

{ 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

Slide 178

Slide 178 text

{ 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 tag 78

Slide 179

Slide 179 text

...Webpack can do (a lot) more! ...Webpack can do (a lot) more! Dev Server Dev Server Tree shaking Tree shaking Dependencies analytics Dependencies analytics Source maps Source maps Async require / module splitting Async require / module splitting @loige 79

Slide 180

Slide 180 text

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 Agenda @loige 80

Slide 181

Slide 181 text

Bundle cache busting Bundle cache busting Device CDN Origin Server bundle.js (original) 81

Slide 182

Slide 182 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js (original) 81

Slide 183

Slide 183 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js (original) 81

Slide 184

Slide 184 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) 81

Slide 185

Slide 185 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) 81

Slide 186

Slide 186 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) bundle.js (cache copy) 81

Slide 187

Slide 187 text

Bundle cache busting Bundle cache busting Device CDN Origin Server example.com bundle.js bundle.js bundle.js (original) bundle.js (cache copy) 81

Slide 188

Slide 188 text

Bundle cache busting 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)

Slide 189

Slide 189 text

Bundle cache busting 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)

Slide 190

Slide 190 text

Bundle cache busting 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

Slide 191

Slide 191 text

STALE! Bundle cache busting 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!

Slide 192

Slide 192 text

Bundle cache busting Bundle cache busting 83

Slide 193

Slide 193 text

Bundle cache busting Bundle cache busting (Manual) Solution 1 (Manual) Solution 1 83

Slide 194

Slide 194 text

Bundle cache busting Bundle cache busting (Manual) Solution 1 (Manual) Solution 1 bundle.js?v=1 bundle.js?v=2 bundle.js?v=3 83

Slide 195

Slide 195 text

Bundle cache busting Bundle cache busting (Manual) Solution 1 (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

Slide 196

Slide 196 text

Bundle cache busting Bundle cache busting 84

Slide 197

Slide 197 text

Bundle cache busting Bundle cache busting (Manual) Solution 2 (Manual) Solution 2 84

Slide 198

Slide 198 text

Bundle cache busting Bundle cache busting (Manual) Solution 2 (Manual) Solution 2 bundle-v1.js bundle-v2.js bundle-v3.js ... 84

Slide 199

Slide 199 text

Bundle cache busting Bundle cache busting (Manual) Solution 2 (Manual) Solution 2 bundle-v1.js bundle-v2.js bundle-v3.js ... Better, but still a lot of diligence and manual effort needed... 84

Slide 200

Slide 200 text

Bundle cache busting Bundle cache busting 85

Slide 201

Slide 201 text

Bundle cache busting Bundle cache busting Webpack Solution Webpack Solution 85

Slide 202

Slide 202 text

Bundle cache busting Bundle cache busting Webpack Solution Webpack Solution output: { filename: '[name].[contenthash].js' } 85

Slide 203

Slide 203 text

Bundle cache busting Bundle cache busting Webpack Solution Webpack Solution ... bundle.9f61f58dd1cc3bb82182.js bundle.aacdf58ef1aa12382199.js bundle.ed61f68defef3bb82221.js output: { filename: '[name].[contenthash].js' } 85

Slide 204

Slide 204 text

Bundle cache busting Bundle cache busting Webpack Solution 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

Slide 205

Slide 205 text

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 Agenda @loige 87

Slide 206

Slide 206 text

Module bundlers are your friends 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

Slide 207

Slide 207 text

Webpack is not Webpack is not the only possibility! the only possibility! 89

Slide 208

Slide 208 text

Ευχαριστώ! Ευχαριστώ! Special thanks: , , , (reviewers) and (inspirations: his and his ) @Podgeypoos79 @andreaman87 @mariocasciaro @eugenserbanescu @MarijnJH amazing book workshop on JS modules @loige Images by cover Image by from Streamline Emoji pack Dimitris Vetsikas Pixabay loige.link/bundle-devit 90