$30 off During Our Annual Pro Sale. View Details »

Codestock: Bundle the Web with Webpack

Codestock: Bundle the Web with Webpack

Jeremy Fairbank

July 16, 2016
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. Bundle
    webpack
    the web with
    Jeremy Fairbank
    @elpapapollo @jfairbank

    View Slide

  2. sigient.com (pushagency.io)
    Your website, SimplyBuilt.
    simplybuilt.com

    View Slide

  3. View Slide

  4. using System;
    using Foo.Bar;
    C#
    import java.lang.*;
    import Foo.Bar;
    Java
    #include
    #include
    C
    require 'json'
    require 'foo_bar'
    Ruby
    import Data.Maybe
    import Data.Sequence as Seq
    Haskell
    EXPLICIT

    View Slide










  5. IMPLICIT

    View Slide

  6. IMPLICIT
    var apiKey = _.get(globalData, 'api.key');
    var date = moment();
    var url = '/api/' + apiKey + '?' + Number(date);
    $.get(url).then(function(data) {
    var el = document.getElementById('main');
    var app = React.createElement(FooBar, {
    data: data
    });
    ReactDOM.render(app, el);
    });

    View Slide

  7. lodash.js
    react.js
    redux.js
    react-dom.js
    jquery.js
    react-redux.js
    moment.js
    foo/bar.js
    app.js

    View Slide

  8. EXPLICIT
    // AMD, require.js
    require(['react', 'foo/bar'], function(React, FooBar) {
    // ...
    });
    // CommonJS, Node
    var React = require('react');
    var FooBar = require('foo/bar');
    // ES2015
    import React from 'react';
    import FooBar from 'foo/bar';

    View Slide

  9. require
    define
    module.exports
    import
    export
    ?
    ? ? ?
    ?

    View Slide

  10. webpack
    MODULE BUNDLER

    View Slide

  11. require
    define
    module.exports
    import
    export

    View Slide

  12. require
    define
    module.exports
    import
    export

    View Slide

  13. require
    define
    module.exports
    import
    export
    bundle.js

    View Slide

  14. View Slide

  15. View Slide

  16. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    const apiKey = getData('api.key');
    const date = moment();
    const url = `/api/${apiKey}?${Number(date)}`;
    $.get(url).then(data => {
    const el = document.getElementById('main');
    const app = ;
    render(app, el);
    });

    View Slide

  17. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';

    View Slide

  18. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';

    View Slide

  19. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';

    View Slide

  20. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main.js
    jquery
    react react-dom
    moment
    foo/bar
    util/data

    … …
    … …

    View Slide

  21. GETTING STARTED
    nodejs.org

    View Slide

  22. $ npm install -g webpack
    $ webpack main.js bundle.js
    Install global binary

    View Slide

  23. $ npm install -g webpack
    $ webpack main.js bundle.js
    Install global binary
    Entry file Output file

    View Slide

  24. // main.js
    const leftPad = require('left-pad');
    const createLogger = require('./modules/log');
    const math = require('./modules/math');
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));
    Demo

    View Slide

  25. CONFIGURATION
    webpack.config.js and run $ webpack
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    };

    View Slide

  26. CONFIGURATION
    webpack.config.js and run $ webpack
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    };

    View Slide

  27. CONFIGURATION
    webpack.config.js and run $ webpack
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    };

    View Slide

  28. CONFIGURATION
    webpack.config.js and run $ webpack
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    };

    View Slide

  29. CONFIGURATION
    webpack.config.js and run $ webpack
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    };
    Demo

    View Slide

  30. MODULE LOADERS
    ES2015

    View Slide

  31. Preprocess
    (Module Loaders)
    Bundle
    import leftPad from 'left-pad';
    import createLogger from './modules/log';
    import * as math from './modules/math';
    var _leftPad = require('left-pad');
    var _log = require('./modules/log');
    var _math = require('./modules/math');

    View Slide

  32. Preprocess
    (Module Loaders)
    Bundle
    import leftPad from 'left-pad';
    import createLogger from './modules/log';
    import * as math from './modules/math';
    var _leftPad = require('left-pad');
    var _log = require('./modules/log');
    var _math = require('./modules/math');

    View Slide

  33. Preprocess
    (Module Loaders)
    Bundle
    import leftPad from 'left-pad';
    import createLogger from './modules/log';
    import * as math from './modules/math';
    var _leftPad = require('left-pad');
    var _log = require('./modules/log');
    var _math = require('./modules/math');

    View Slide

  34. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015

    View Slide

  35. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015

    View Slide

  36. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015

    View Slide

  37. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015

    View Slide

  38. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015

    View Slide

  39. $ npm install --save-dev \
    babel-loader babel-core babel-preset-es2015
    {
    "presets": ["es2015"]
    }
    .babelrc or babel key in package.json

    View Slide

  40. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };

    View Slide

  41. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };

    View Slide

  42. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };

    View Slide

  43. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };

    View Slide

  44. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };

    View Slide

  45. module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    module: {
    loaders: [
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel',
    },
    ],
    },
    };
    Demo

    View Slide

  46. import leftPad from 'left-pad';
    import createLogger from './modules/log';
    import * as math from './modules/math';
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));
    RESOLUTION

    View Slide

  47. import leftPad from 'left-pad';
    import createLogger from './modules/log';
    import * as math from './modules/math';
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));
    RESOLUTION

    View Slide

  48. const path = require('path');
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    resolve: {
    root: [
    path.join(__dirname, 'modules'),
    ],
    },
    module: { ... },
    };

    View Slide

  49. const path = require('path');
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    resolve: {
    root: [
    path.join(__dirname, 'modules'),
    ],
    },
    module: { ... },
    };

    View Slide

  50. const path = require('path');
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    resolve: {
    root: [
    path.join(__dirname, 'modules'),
    ],
    },
    module: { ... },
    };

    View Slide

  51. const path = require('path');
    module.exports = {
    entry: './main.js',
    output: {
    filename: './bundle.js',
    },
    resolve: {
    root: [
    path.join(__dirname, 'modules'),
    ],
    },
    module: { ... },
    };
    Must be absolute path!

    View Slide

  52. RESOLUTION
    import leftPad from 'left-pad';
    import createLogger from 'log';
    import * as math from 'math';
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));

    View Slide

  53. RESOLUTION
    import leftPad from 'left-pad';
    import createLogger from 'log';
    import * as math from 'math';
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));

    View Slide

  54. RESOLUTION
    import leftPad from 'left-pad';
    import createLogger from 'log';
    import * as math from 'math';
    const logger = createLogger(document.getElementById('log'));
    logger.log('29 =', leftPad(Number(29).toString(2), 8, 0));
    logger.log('40 + 2 =', math.add(40, 2));
    logger.log('10 x 2 =', math.multiply(10, 2));
    ?

    View Slide

  55. import leftPad from 'left-pad';
    import createLogger from 'log';
    import * as math from 'math';
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    $ npm install --save left-pad
    $ npm install --save \
    jquery react react-dom \
    moment
    Automatically resolve to node_modules

    View Slide

  56. import leftPad from 'left-pad';
    import createLogger from 'log';
    import * as math from 'math';
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    $ npm install --save left-pad
    $ npm install --save \
    jquery react react-dom \
    moment
    Automatically resolve to node_modules

    View Slide

  57. MULTIPLE ENTRIES






    <br/>


    main.html






    <br/>


    numbers.html

    View Slide

  58. MULTIPLE ENTRIES
    main.js
    main.bundle.js
    numbers.js
    numbers.bundle.js
    webpack.main.config.js webpack.numbers.config.js

    View Slide

  59. MULTIPLE ENTRIES
    main.js
    main.bundle.js
    numbers.js
    numbers.bundle.js
    webpack.main.config.js webpack.numbers.config.js
    x

    View Slide

  60. MULTIPLE ENTRIES
    main.js numbers.js
    main.bundle.js numbers.bundle.js

    View Slide

  61. const path = require('path');
    module.exports = {
    entry: {
    main: './main.js',
    numbers: './numbers.js',
    },
    output: {
    filename: '[name].bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    MULTIPLE ENTRIES

    View Slide

  62. const path = require('path');
    module.exports = {
    entry: {
    main: './main.js',
    numbers: './numbers.js',
    },
    output: {
    filename: '[name].bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    MULTIPLE ENTRIES

    View Slide

  63. const path = require('path');
    module.exports = {
    entry: {
    main: './main.js',
    numbers: './numbers.js',
    },
    output: {
    filename: '[name].bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    MULTIPLE ENTRIES

    View Slide

  64. const path = require('path');
    module.exports = {
    entry: {
    main: './main.js',
    numbers: './numbers.js',
    },
    output: {
    filename: '[name].bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    MULTIPLE ENTRIES

    View Slide

  65. const path = require('path');
    module.exports = {
    entry: {
    main: './main.js',
    numbers: './numbers.js',
    },
    output: {
    filename: '[name].bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    MULTIPLE ENTRIES
    Demo

    View Slide

  66. DEBUGGING

    View Slide

  67. var numbers = [1, 2, 3, 4, 5];
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;
    try {
    for (var _iterator = numbers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var n = _step.value;
    console.log(n.toStrin());
    }
    } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
    } finally {
    try {
    if (!_iteratorNormalCompletion && _iterator.return) {
    _iterator.return();
    }
    } finally {
    if (_didIteratorError) {
    throw _iteratorError;
    }
    }
    }
    const numbers = [1, 2, 3, 4, 5];
    for (const n of numbers) {
    console.log(n.toStrin());
    }

    View Slide

  68. var numbers = [1, 2, 3, 4, 5];
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;
    try {
    for (var _iterator = numbers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var n = _step.value;
    console.log(n.toStrin());
    }
    } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
    } finally {
    try {
    if (!_iteratorNormalCompletion && _iterator.return) {
    _iterator.return();
    }
    } finally {
    if (_didIteratorError) {
    throw _iteratorError;
    }
    }
    }
    const numbers = [1, 2, 3, 4, 5];
    for (const n of numbers) {
    console.log(n.toStrin());
    }
    TypeError

    View Slide

  69. SOURCE MAPS
    var numbers = [1, 2, 3, 4, 5];
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;
    try {
    for (var _iterator = numbers[Symbol.iterator](), _step;
    !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
    _iteratorNormalCompletion = true) {
    var n = _step.value;
    console.log(n.toStrin());
    }
    } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
    } finally {
    try {
    if (!_iteratorNormalCompletion && _iterator.return) {
    _iterator.return();
    }
    } finally {
    if (_didIteratorError) {
    throw _iteratorError;
    }
    }
    }

    View Slide

  70. SOURCE MAPS
    var numbers = [1, 2, 3, 4, 5];
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;
    try {
    for (var _iterator = numbers[Symbol.iterator](), _step;
    !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
    _iteratorNormalCompletion = true) {
    var n = _step.value;
    console.log(n.toStrin());
    }
    } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
    } finally {
    try {
    if (!_iteratorNormalCompletion && _iterator.return) {
    _iterator.return();
    }
    } finally {
    if (_didIteratorError) {
    throw _iteratorError;
    }
    }
    }
    const numbers = [1, 2, 3, 4, 5];
    for (const n of numbers) {
    console.log(n.toStrin());
    }

    View Slide

  71. SOURCE MAPS
    main.js
    bundle.js bundle.js.map
    main.js
    bundle.js
    Inline
    Separate

    View Slide

  72. const path = require('path');
    module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: {
    filename: 'bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    SOURCE MAPS

    View Slide

  73. const path = require('path');
    module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: {
    filename: 'bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    SOURCE MAPS

    View Slide

  74. const path = require('path');
    module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: {
    filename: 'bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };
    SOURCE MAPS
    Demo

    View Slide

  75. DEVELOPMENT
    webpack-dev-server

    View Slide

  76. webpack-dev-server

    View Slide

  77. webpack-dev-server
    bundle.js?

    View Slide

  78. webpack-dev-server
    bundle.js?

    View Slide

  79. webpack-dev-server
    main.js
    left-pad
    log
    math

    View Slide

  80. webpack-dev-server
    main.js
    left-pad
    log
    math

    View Slide

  81. webpack-dev-server
    main.js
    left-pad
    log
    math
    !

    View Slide

  82. webpack-dev-server

    View Slide

  83. webpack-dev-server
    The bundle
    updated!

    View Slide

  84. webpack-dev-server
    bundle.js?

    View Slide

  85. webpack-dev-server
    bundle.js?

    View Slide

  86. webpack-dev-server
    $ npm install -g webpack-dev-server
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline

    View Slide

  87. webpack-dev-server
    $ npm install -g webpack-dev-server
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline

    View Slide

  88. webpack-dev-server
    $ npm install -g webpack-dev-server
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline

    View Slide

  89. webpack-dev-server
    $ npm install -g webpack-dev-server
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline

    View Slide

  90. webpack-dev-server
    $ npm install -g webpack-dev-server
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline
    !

    View Slide

  91. webpack-dev-server
    const path = require('path');
    module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: {
    publicPath: 'http://127.0.0.1:8080/assets/',
    filename: 'bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };

    View Slide

  92. webpack-dev-server
    const path = require('path');
    module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: {
    publicPath: 'http://127.0.0.1:8080/assets/',
    filename: 'bundle.js',
    },
    resolve: { ... },
    module: { ... },
    };

    View Slide








  93. <br/>


    webpack-dev-server

    View Slide








  94. <br/>


    webpack-dev-server

    View Slide








  95. <br/>


    webpack-dev-server
    Demo

    View Slide

  96. webpack-dev-server
    Hot Module Replacement (HMR)

    View Slide

  97. HOT MODULE REPLACEMENT
    main.js
    hobbies
    log

    View Slide

  98. HOT MODULE REPLACEMENT
    bundle.js? main.js
    hobbies
    log

    View Slide

  99. HOT MODULE REPLACEMENT
    bundle.js? main.js
    hobbies
    log

    View Slide

  100. log hobbies
    HOT MODULE REPLACEMENT
    main.js
    hobbies
    log

    View Slide

  101. log hobbies
    HOT MODULE REPLACEMENT
    main.js
    hobbies
    log

    View Slide

  102. log hobbies
    HOT MODULE REPLACEMENT
    main.js
    hobbies
    log
    !
    hobbies

    View Slide

  103. log hobbies
    HOT MODULE REPLACEMENT
    main.js
    hobbies
    log
    ! Swap this
    module out!
    hobbies

    View Slide

  104. HOT MODULE REPLACEMENT
    $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline
    --hot

    View Slide

  105. $ webpack-dev-server \
    --host 127.0.0.1
    --port 8080
    --inline
    --hot
    HOT MODULE REPLACEMENT

    View Slide

  106. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  107. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  108. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  109. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  110. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  111. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  112. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  113. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    HOT MODULE REPLACEMENT

    View Slide

  114. import defaultHobbies from './hobbies';
    function logHobbies(hobbies) {
    logger.log('Hobbies:');
    hobbies.forEach(hobby => {
    logger.log(hobby);
    });
    }
    if (module.hot) {
    module.hot.accept('./hobbies', () => {
    const newHobbies = require('./hobbies').default;
    logHobbies(newHobbies);
    });
    }
    logHobbies(defaultHobbies);
    Demo
    HOT MODULE REPLACEMENT

    View Slide

  115. HOT MODULE
    REPLACEMENT*

    View Slide

  116. HOT MODULE REPLACEMENT*
    *Pure components and persistent state
    • github.com/gaearon/react-hot-loader
    • github.com/yargalot/Angular-HMR
    • github.com/mgechev/angular2-hot-loader
    • github.com/AngularClass/angular2-hmr

    View Slide

  117. HOT MODULE REPLACEMENT*
    Demo

    View Slide

  118. CSS MODULES

    View Slide

  119. /* General */
    .highlight {
    background: rgba(255, 0, 0, 0.2);
    border: 4px solid rgba(255, 0, 0, 0.4);
    color: #c00;
    font-style: italic;
    padding: 12px;
    }
    /* Data Grid */
    .datagrid tr.highlight td {
    background-color: #ccc;
    border-top: 1px solid #999;
    }

    View Slide

  120. /* General */
    .highlight {
    background: rgba(255, 0, 0, 0.2);
    border: 4px solid rgba(255, 0, 0, 0.4);
    color: #c00;
    font-style: italic;
    padding: 12px;
    }
    /* Data Grid */
    .datagrid tr.highlight td {
    background-color: #ccc;
    border-top: 1px solid #999;
    }

    View Slide

  121. BEM
    OOCSS
    SMACSS

    View Slide

  122. CSS MODULES
    datagrid.css
    general.css

    View Slide

  123. $ npm install --save-dev css-loader style-loader
    CSS MODULES

    View Slide

  124. module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: { ... },
    resolve: { ... },
    module: {
    loaders: [
    ...
    {
    test: /\.css$/,
    loader: 'style!css?modules',
    },
    ],
    },
    };

    View Slide

  125. module.exports = {
    devtool: '#inline-source-map',
    entry: './main.js',
    output: { ... },
    resolve: { ... },
    module: {
    loaders: [
    ...
    {
    test: /\.css$/,
    loader: 'style!css?modules',
    },
    ],
    },
    };

    View Slide

  126. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  127. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  128. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  129. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  130. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  131. {
    test: /\.css$/,
    loader: 'style
    ! css
    ? modules',
    },

    View Slide

  132. CSS MODULES
    datagrid.css
    general.css
    css-loader
    style-loader
    <br/>...<br/>

    View Slide

  133. CSS MODULES
    import React, { Component, PropTypes } from 'react';
    import styles from './styles.css';

    View Slide

  134. CSS MODULES
    import React, { Component, PropTypes } from 'react';
    import styles from './styles.css';

    View Slide

  135. CSS MODULES
    import React, { Component, PropTypes } from 'react';
    import styles from './styles.css';

    View Slide

  136. CSS MODULES
    Demo
    import React, { Component, PropTypes } from 'react';
    import styles from './styles.css';

    View Slide

  137. PRODUCTION

    View Slide

  138. main.js
    main.bundle.js
    main.js
    main-81ca0155911e480de735.js
    webpack.dev.config.js webpack.prod.config.js
    DEVELOPMENT PRODUCTION

    View Slide

  139. module.exports = {
    ...
    plugins: [
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    }),
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    new ExtractTextPlugin('main-[chunkhash].css'),
    new webpack.optimize.UglifyJsPlugin({
    minimize: true,
    }),
    ],
    };

    View Slide

  140. module.exports = {
    ...
    plugins: [
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    }),
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    new ExtractTextPlugin('main-[chunkhash].css'),
    new webpack.optimize.UglifyJsPlugin({
    minimize: true,
    }),
    ],
    };

    View Slide

  141. DEBUGGING
    PERFORMANCE ENHANCEMENTS
    FEATURE FLAGS
    DefinePlugin

    View Slide

  142. const webpack = require('webpack');
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    'NEW_FEATURE': false,
    });
    const webpack = require('webpack');
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"development"',
    '__DEV__': true,
    'NEW_FEATURE': true,
    }),
    webpack.prod.config.js
    webpack.dev.config.js

    View Slide

  143. if (process.env.NODE_ENV !== 'production') {
    console.log('DEBUG MESSAGE');
    }
    if (__DEV__) {
    console.log('IN DEVELOPMENT MODE');
    }
    if (NEW_FEATURE) {
    newFeature.run();
    }

    View Slide

  144. if (false) {
    console.log('DEBUG MESSAGE');
    }
    if (false) {
    console.log('IN DEVELOPMENT MODE');
    }
    if (false) {
    newFeature.run();
    }

    View Slide

  145. module.exports = {
    ...
    plugins: [
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    }),
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    new ExtractTextPlugin('main-[chunkhash].css'),
    new webpack.optimize.UglifyJsPlugin({
    minimize: true,
    }),
    ],
    };

    View Slide

  146. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js

    View Slide

  147. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js

    View Slide

  148. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js
    react
    jquery
    react-dom

    View Slide

  149. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-f571e3836f6a11af10e0.js
    react
    jquery
    react-dom

    View Slide

  150. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-f571e3836f6a11af10e0.js

    View Slide

  151. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-f571e3836f6a11af10e0.js
    react
    jquery
    react-dom

    View Slide

  152. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js
    vendor-81ca0155911e480de735.js

    View Slide

  153. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js
    vendor-81ca0155911e480de735.js

    View Slide

  154. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js
    react
    jquery
    react-dom
    vendor-81ca0155911e480de735.js

    View Slide

  155. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-81ca0155911e480de735.js
    main-f571e3836f6a11af10e0.js
    react
    jquery
    react-dom
    vendor-81ca0155911e480de735.js

    View Slide

  156. // main.js
    import $ from 'jquery';
    import React from 'react';
    import { render } from 'react-dom';
    import moment from 'moment';
    import FooBar from 'foo/bar';
    import { getData } from 'util/data';
    main-f571e3836f6a11af10e0.js
    react
    jquery
    react-dom
    vendor-81ca0155911e480de735.js

    View Slide

  157. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  158. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  159. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  160. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  161. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  162. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  163. module.exports = {
    entry: {
    main: './main.js',
    vendor: ['jquery', 'react', 'react-dom'],
    },
    output: {
    filename: '[name]-[chunkhash].js',
    },
    plugins: [
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    ],
    };

    View Slide

  164. module.exports = {
    ...
    plugins: [
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    }),
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    new ExtractTextPlugin('main-[chunkhash].css'),
    new webpack.optimize.UglifyJsPlugin({
    minimize: true,
    }),
    ],
    };

    View Slide

  165. EXTRACT CSS
    datagrid.css
    header.css
    css-loader
    style-loader
    <br/>...<br/>

    View Slide

  166. datagrid.css
    header.css
    css-loader
    style-loader
    main-81ca0155911e480de735.css
    EXTRACT CSS

    View Slide

  167. $ npm install --save-dev \
    extract-text-webpack-plugin
    EXTRACT CSS

    View Slide

  168. const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    module: {
    loaders: [
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules'),
    },
    ],
    },
    plugins: [
    new ExtractTextPlugin('main-[chunkhash].css'),
    ],
    };

    View Slide

  169. const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    module: {
    loaders: [
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules'),
    },
    ],
    },
    plugins: [
    new ExtractTextPlugin('main-[chunkhash].css'),
    ],
    };

    View Slide

  170. const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    module: {
    loaders: [
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules'),
    },
    ],
    },
    plugins: [
    new ExtractTextPlugin('main-[chunkhash].css'),
    ],
    };

    View Slide

  171. const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    module: {
    loaders: [
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules'),
    },
    ],
    },
    plugins: [
    new ExtractTextPlugin('main-[chunkhash].css'),
    ],
    };

    View Slide

  172. const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    module: {
    loaders: [
    {
    test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules'),
    },
    ],
    },
    plugins: [
    new ExtractTextPlugin('main-[chunkhash].css'),
    ],
    };

    View Slide

  173. module.exports = {
    ...
    plugins: [
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': '"production"',
    '__DEV__': false,
    }),
    new webpack.optimize.CommonsChunkPlugin(
    'vendor',
    'vendor-[chunkhash].js'
    ),
    new ExtractTextPlugin('main-[chunkhash].css'),
    new webpack.optimize.UglifyJsPlugin({
    minimize: true,
    }),
    ],
    };

    View Slide

  174. if (process.env.NODE_ENV !== 'production') {
    console.log('DEBUG MESSAGE');
    }
    if (__DEV__) {
    console.log('IN DEVELOPMENT MODE');
    }
    if (NEW_FEATURE) {
    newFeature.run();
    }
    DEAD CODE

    View Slide

  175. DEAD CODE
    if (false) {
    console.log('DEBUG MESSAGE');
    }
    if (false) {
    console.log('IN DEVELOPMENT MODE');
    }
    if (false) {
    newFeature.run();
    }

    View Slide

  176. DEAD CODE
    if (false) {
    console.log('DEBUG MESSAGE');
    }
    if (false) {
    console.log('IN DEVELOPMENT MODE');
    }
    if (false) {
    newFeature.run();
    }
    x

    View Slide

  177. RESOURCES
    • webpack.github.io
    • github.com/gaearon/react-hot-loader
    • github.com/yargalot/Angular-HMR
    • github.com/mgechev/angular2-hot-loader
    • github.com/AngularClass/angular2-hmr
    • browserify.org
    • rollupjs.org

    View Slide

  178. THANKS!
    Jeremy Fairbank
    @elpapapollo @jfairbank
    Code: bit.ly/29XKTCW

    View Slide