Codestock: Bundle the Web with Webpack

Codestock: Bundle the Web with Webpack

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

July 16, 2016
Tweet

Transcript

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

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

  3. None
  4. using System; using Foo.Bar; C# import java.lang.*; import Foo.Bar; Java

    #include <stdio.h> #include <foo_bar.h> C require 'json' require 'foo_bar' Ruby import Data.Maybe import Data.Sequence as Seq Haskell EXPLICIT
  5. <script src="jquery.js"></script> <script src="lodash.js"></script> <script src="react.js"></script> <script src="react-dom.js"></script> <script src="redux.js"></script>

    <script src="react-redux.js"></script> <script src="moment.js"></script> <script src="foo/bar.js"></script> <script src="app.js"></script> IMPLICIT
  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); });
  7. lodash.js react.js redux.js react-dom.js jquery.js react-redux.js moment.js foo/bar.js app.js

  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';
  9. require define module.exports import export ? ? ? ? ?

  10. webpack MODULE BUNDLER

  11. require define module.exports import export

  12. require define module.exports import export

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

  14. None
  15. None
  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 = <FooBar data={data} />; render(app, el); });
  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';
  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';
  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';
  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 … … … … …
  21. GETTING STARTED nodejs.org

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

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

    global binary Entry file Output file
  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
  25. CONFIGURATION webpack.config.js and run $ webpack module.exports = { entry:

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

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

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

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

    './main.js', output: { filename: './bundle.js', }, }; Demo
  30. MODULE LOADERS ES2015

  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');
  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');
  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');
  34. $ npm install --save-dev \ babel-loader babel-core babel-preset-es2015

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

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

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

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

  39. $ npm install --save-dev \ babel-loader babel-core babel-preset-es2015 { "presets":

    ["es2015"] } .babelrc or babel key in package.json
  40. module.exports = { entry: './main.js', output: { filename: './bundle.js', },

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

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

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

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

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

    module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', }, ], }, }; Demo
  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
  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
  48. const path = require('path'); module.exports = { entry: './main.js', output:

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

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

    { filename: './bundle.js', }, resolve: { root: [ path.join(__dirname, 'modules'), ], }, module: { ... }, };
  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!
  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));
  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));
  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)); ?
  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
  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
  57. MULTIPLE ENTRIES <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head>

    <body> <script src="main.bundle.js"> </script> </body> </html> main.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <script src="numbers.bundle.js"> </script> </body> </html> numbers.html
  58. MULTIPLE ENTRIES main.js main.bundle.js numbers.js numbers.bundle.js webpack.main.config.js webpack.numbers.config.js

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

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

  61. const path = require('path'); module.exports = { entry: { main:

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

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

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

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

    './main.js', numbers: './numbers.js', }, output: { filename: '[name].bundle.js', }, resolve: { ... }, module: { ... }, }; MULTIPLE ENTRIES Demo
  66. DEBUGGING

  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()); }
  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
  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; } } }
  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()); }
  71. SOURCE MAPS main.js bundle.js bundle.js.map main.js bundle.js Inline Separate

  72. const path = require('path'); module.exports = { devtool: '#inline-source-map', entry:

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

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

    './main.js', output: { filename: 'bundle.js', }, resolve: { ... }, module: { ... }, }; SOURCE MAPS Demo
  75. DEVELOPMENT webpack-dev-server

  76. webpack-dev-server

  77. webpack-dev-server bundle.js?

  78. webpack-dev-server bundle.js?

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

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

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

  82. webpack-dev-server

  83. webpack-dev-server The bundle updated!

  84. webpack-dev-server bundle.js?

  85. webpack-dev-server bundle.js?

  86. webpack-dev-server $ npm install -g webpack-dev-server $ webpack-dev-server \ --host

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

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

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

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

    127.0.0.1 --port 8080 --inline !
  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: { ... }, };
  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: { ... }, };
  93. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <div

    id="log"></div> <script src="http://127.0.0.1:8080/assets/bundle.js"> </script> </body> </html> webpack-dev-server
  94. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <div

    id="log"></div> <script src="http://127.0.0.1:8080/assets/bundle.js"> </script> </body> </html> webpack-dev-server
  95. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <div

    id="log"></div> <script src="http://127.0.0.1:8080/assets/bundle.js"> </script> </body> </html> webpack-dev-server Demo
  96. webpack-dev-server Hot Module Replacement (HMR)

  97. HOT MODULE REPLACEMENT main.js hobbies log

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

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

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

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

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

  103. log hobbies HOT MODULE REPLACEMENT main.js hobbies log ! Swap

    this module out! hobbies
  104. HOT MODULE REPLACEMENT $ webpack-dev-server \ --host 127.0.0.1 --port 8080

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

    MODULE REPLACEMENT
  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
  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
  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
  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
  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
  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
  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
  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
  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
  115. HOT MODULE REPLACEMENT*

  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
  117. HOT MODULE REPLACEMENT* Demo

  118. CSS MODULES

  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; }
  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; }
  121. BEM OOCSS SMACSS

  122. CSS MODULES datagrid.css general.css

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

  124. module.exports = { devtool: '#inline-source-map', entry: './main.js', output: { ...

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

    }, resolve: { ... }, module: { loaders: [ ... { test: /\.css$/, loader: 'style!css?modules', }, ], }, };
  126. { test: /\.css$/, loader: 'style ! css ? modules', },

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

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

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

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

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

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

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

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

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

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

    'react'; import styles from './styles.css';
  137. PRODUCTION

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

  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, }), ], };
  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, }), ], };
  141. DEBUGGING PERFORMANCE ENHANCEMENTS FEATURE FLAGS DefinePlugin

  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
  143. if (process.env.NODE_ENV !== 'production') { console.log('DEBUG MESSAGE'); } if (__DEV__)

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

    DEVELOPMENT MODE'); } if (false) { newFeature.run(); }
  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, }), ], };
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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' ), ], };
  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' ), ], };
  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' ), ], };
  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' ), ], };
  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' ), ], };
  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' ), ], };
  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' ), ], };
  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, }), ], };
  165. EXTRACT CSS datagrid.css header.css css-loader style-loader <style> ... </style>

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

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

  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'), ], };
  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'), ], };
  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'), ], };
  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'), ], };
  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'), ], };
  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, }), ], };
  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
  175. DEAD CODE if (false) { console.log('DEBUG MESSAGE'); } if (false)

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

    { console.log('IN DEVELOPMENT MODE'); } if (false) { newFeature.run(); } x
  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
  178. THANKS! Jeremy Fairbank @elpapapollo @jfairbank Code: bit.ly/29XKTCW