Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Front-End build tools - Webpack

Front-End build tools - Webpack

Set up your first build with Webpack

Răzvan Roșu

July 26, 2018
Tweet

More Decks by Răzvan Roșu

Other Decks in Programming

Transcript

  1. What are build tools? Definition: Build tools are programs that

    automate the process of creating applications by: compiling, packaging code, etc
  2. Why should we use build tools? Generic reasons: - Accelerate

    the development process - Automate repeating tasks - Optimize the application - Package our application for deployment
  3. Why should we use build tools? Front-End specific reasons: -

    JavaScript hardly scales (scope, readability, size issue) - Browsers have a bottleneck in regards to HTTP requests (HTTP 1.1: Chrome 6, Firefox 6, Safari 6, IE 8 simultaneous persistent connections per server/proxy)
  4. Why Webpack? - Other tools’ main purpose is to concatenate

    files. On any change, these tools perform a full rebuild. Concatenation causes dead code. e.g.: pulling Lodash and momentJS into a project - Multiple IIFEs (Immediately Invoked Function Expressions) are slow. Bundlers early on were creating lots of IIFEs. Task runners can’t lazy load.
  5. Why Webpack? and because... What module loaders do you regularly

    use, if any? - JetBrains 2018 Survey https://www.jetbrains.com/research/devecosystem-2018/javascript/
  6. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  7. Preparations: Prerequisites: - Node ^5 (I recommend using NVM) -

    NPM ^3.3.6 (I recommend Yarn) Webpack available versions: - 4.8.3 latest - 3.12.0 - 2.7.0 - 1.15.0 @ 20-05-2018
  8. Install Webpack > touch index.html > touch index.js > npm

    init > yarn add webpack@^1.15.0 --dev
  9. Bundle up! If Webpack is installed globally: > webpack index.js

    ./bundle.js If Webpack is installed locally: > ./node_modules/.bin/webpack index.js ./bundle.js Best practice: define a NPM script and use config file.
  10. Configuring Webpack > touch webpack.config.js Basic configuration: module.exports = {

    entry: './index.js', output: { filename: './bundle.js' } }; Add NPM script in package.json Basic script: "scripts": { "start": "webpack" }
  11. Webpack Loaders Webpack Loaders are plugins. There’s a loader for

    almost everything Official list of loaders: https://github.com/webpack/docs/wiki/list-of-loaders
  12. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  13. ES6 transpile > yarn add babel-loader@^6.4.1 babel-preset-2015 babel-core --dev babel-loader

    versions: 8.0.0 beta 7.1.4 (doesn’t work with webpack 1.15.0) 6.4.1 @20-05-2018 babel-core versions: 7.0.0 beta 6.26.3 @20-05-2018
  14. ES6 transpile Extend webpack.config.js for: transpiling ES6 and separate build

    from development files. const path = require('path'); module.exports = { entry: './index.js', output: { path: path.join(__dirname, 'build'), filename: 'index.bundle.js' }, module: { loaders: [ { test: /\.js$/, loader: 'babel', exclude: /node_modules/, query: { presets: ['es2015'] } } ]}};
  15. ES6 transpile Notes: • Until Babel 5, all transformations were

    automatically transpiled. Starting with Babel 6, everything is opt-in. • Available transforms: react, es2015, ecmascript stages. Add these in .babelrc if needed. > touch .babelrc { "presets": ["es2015", "stage-1"] }
  16. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  17. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  18. Preprocessors and Postprocessors > yarn add style-loader css-loader --dev >

    yarn add sass-loader node-sass autoprefixer-loader --dev > yarn add extract-text-webpack-plugin@^1.0.1 --dev Available version: style-loader 0.21.0 css-loader 0.28.11 node-sass 4.9.0 sass-loader 7.0.1 @28-05-2018 Available version: autoprefixer-loader 3.2.0 extract-text-webpack-plugin 3.0.2 latest 1.0.1 (webpack ^1.*)
  19. Preprocessors and Postprocessors We preprocess SCSS into CSS w/ sass-loader,

    node-sass. We load CSS into the document’s head w/ style-loader, css-loader. We postprocess the properties with desired vendor prefixes w/ autoprefixer-loader We import the output styles into the file via a link tag instead of inlining w/ extract-text-webpack-plugin.
  20. Preprocessors and Postprocessors Let’s add some SCSS files… > touch

    src/master.scss styles/_colors.scss styles/_typography.scss
  21. Preprocessors and Postprocessors Let’s configure the loaders const ExtractTextPlugin =

    require("extract-text-webpack-plugin"); module.exports = { entry: { app: ['./src/index.js','./src/master.scss'] }, output: { path: path.join(__dirname, 'build'), filename: '[name].bundle.js' }, module.exports = { ... module: { loaders: [ { test: /\.scss$/, loader: ExtractTextPlugin.extract('style', 'css-loader!autoprefixer?browsers=last 2 versions!sass') } ] }, plugins: [new ExtractTextPlugin("[name].bundle.css")]
  22. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  23. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  24. Development server Configuring the plugin webpack.config.js module.exports = { ...

    devServer: { inline: true, port: 1337 }, … }; Change NPM scripts package.json "scripts": { "start": "webpack-dev-server", "build:prod": "webpack", "clean": "rm -rf build" },
  25. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  26. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  27. File watcher + Hot Module Replacement* File watching is a

    built-in feature of Webpack. In order to enable it, we add a --watch. package.json "scripts": { "start": "webpack-dev-server", "build:dev": "webpack --watch", "build:prod": "webpack", "clean": "rm -rf build" }, Thus: > npm run build:dev > npm start Note! Don’t forget to: - require the master.scss file in index.js for parsing - include the bundles in your index.html file
  28. File watcher + Hot Module Replacement* HMR (Hot Module replacement)

    allows webpack to reload only chunks of JS code, instead of the whole code base. It can be used with Redux or specific framework loaders for HML. e.g.: react-hot-loader, vue-hot-loader
  29. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  30. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  31. Loading assets: images and fonts Q: Why use a Loader

    for images? A: Performance Q: How is it better performing? A: The images are encrypted into base64 and injected into the DOM tree. In other words less HTTP requests. Q: Is there a downside? A: Yes, the image source is assigned via JS. You will manage the images from outside HTML and crawlers will not be able to reach it without prerendering.
  32. Loading assets: images and fonts > yarn add url-loader file-loader

    --dev Available versions: url-loader 1.0.1 file-loader 1.1.11 @28-05-2018
  33. Loading assets: images and fonts { test: /\.(png|jp(e*)g|svg)$/, loader: 'url-loader?limit=900000'

    }, { test: /\.woff2?(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader', use: { options: { mimetype: 'application/font-woff', name: "./src/fonts/[name].[ext]" } } }
  34. Loading assets: images and fonts index.html <img id="manticore-img" alt="Manticore" title="Manticore">

    index.js import manticoreimg from './images/manticore.png'; const manticore = document.getElementById('manticore-img'); if (manticore) manticore.src = manticoreimg; import pattern from './images/pattern.png'; master.scss footer { background: $color-main url('images/pattern.png') left top repeat-x; }
  35. Loading assets: images and fonts typography.scss @font-face { font-family: 'Nunito';

    ...} @font-face { font-family: 'Space Mono'; ...} $font-main: 'Space Mono', monospace; $font-secondary: 'Nunito', sans-serif;
  36. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  37. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  38. commons.bundle.js OR vendors.bundle.js Vendor bundles and Code splitting Q: What

    is Code Splitting? A: Multiple bundle files setup page1.js page2.js page3.js page1.bundle.js page2.bundle.js page3.bundle.js
  39. Vendor bundles and Code splitting We will use CommonsChunks, a

    preinstalled Webpack plugin. Possible configurations: - Detect repeating dependencies in all entries and split into commons.bundle.js + unique bundles - Manually define dependencies for a vendor.bundle.js which should be included on every page.
  40. Vendor bundles and Code splitting webpack.config.js const webpack = require('webpack');

    const CommonsChunkPlugin = require('./node_modules/webpack/lib/optimize/CommonsChunkPlugin'); module.exports = { entry: { app: [ './src/index.js', './src/master.scss' ], page1: './src/scripts/page1.js', page2: './src/scripts/page2.js', vendor: ['angular'] }, ... module.exports = { ... plugins: { new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js', minChunks: Infinity }), }
  41. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  42. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  43. Minify JS and CSS + source maps Source maps are

    a built-in feature. Source maps establish the connection between bundles and the original files. They have a performance drawback. It is best practice to disable them in production. webpack.config.js module.exports = { ... devtool: 'source-map', ... }
  44. Minify JS and CSS + source maps In order to

    map bundled CSS with SCSS, we need to modify it’s loader params module: { loaders: [ {loader: ExtractTextPlugin.extract('style', 'css-loader?sourceMap!autoprefixer?browsers=last 2 versions!sass)} ]}
  45. Minify JS and CSS + source maps We can minify

    (uglify) bundles with another built-in plugin: UglifyJsPlugin. const UglifyJsPlugin = require('./node_modules/webpack/lib/optimize/UglifyJsPlugin'); module.exports = { ... plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, drop_console: false } }) ] }
  46. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  47. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  48. ENV variables Environment variables are not a Webpack feature. We

    can send value through NPM scripts. "scripts": { "start": "NODE_ENV=production webpack-dev-server", "build:dev": "NODE_ENV=development webpack-dev-server --watch", "build:prod": "NODE_ENV=production webpack", "build:debugprod": "NODE_ENV=debugproduction webpack-dev-server --watch", "clean": "rm -rf build" },
  49. ENV variables We can catch inside webpack.config.js the values send

    through NPM scripts as follows: /** * ENV variables */ const env = process.env.NODE_ENV; const isDev = env === 'development'; const isProd = env === 'production'; const debugProd = env === 'debugproduction';
  50. ENV variables Based on our needs, we define specific behaviour

    for production and development. e.g.: devtool: isProd ? false : 'source-map', (isDev || debugProd) ? module.exports.plugins.push(new BundleAnalyzerPlugin()) : '';
  51. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  52. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  53. Cache invalidation with hashes Webpack offers several placeholders. We’ve used

    [name] before for passing the same filename from dev files to bundles. We can also use [chunkhash] and [contenthash] to add a hash within the filenames.
  54. Cache invalidation with hashes We will update our build filenames

    as follows: module.exports = { ... output: { path: path.join(__dirname, 'build'), filename: '[name].bundle.[chunkhash:4].js' }, … plugins: [ new ExtractTextPlugin('[name].min.[contenthash:4].css'), … ]
  55. Cache invalidation with hashes Webpack can inject the bundle names

    (with autogenerated hashes) dynamically with a loader. > yarn add html-webpack-plugin --dev
  56. Cache invalidation with hashes Configure html-webpack-plugin: const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = { ... plugins: [ ... new HtmlWebpackPlugin({ template: path.join(__dirname, 'index.html'), filename: 'index.html', chunks: ['vendor', 'app'] }), ... ]
  57. Cache invalidation with hashes Note: Remove from index.html the CSS

    link and bundle scripts! A new index.html file will be created within build, with the hashed bundles injected and served in the browser.
  58. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  59. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  60. Internationlization i18n* There are all sorts of i18n loaders based

    on the framework that you will use in your project. i18n-express react-i18next @ngx-translate/core
  61. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  62. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  63. GZip compression We can compile our assets into binary files

    e.g.: vendor.bundle.js -> vendor.bundle.js.gz > yarn add compression-webpack-plugin --dev
  64. GZip compression webpack.config.js const CompressionPlugin = require("compression-webpack-plugin"); module.exports = {

    ... new CompressionPlugin({ asset : "[path].gz[query]", algorithm : "gzip", test : /\.js$|\.css$|\.html$/, threshold : 10240, minRatio : 0.8 }) }
  65. GZip compression Note: Serving gzip files can be done through

    NGINX. Binary files aren’t supported by all browsers. NGINX can provide the normal file as fallback.
  66. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  67. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent
  68. Performance monitoring We can have a visual preview of our

    bundles with the help of webpack-bundle-analyzer plugin.
  69. Performance monitoring > yarn add webpack-bundle-analyzer --dev webpack.config.js const BundleAnalyzerPlugin

    = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { plugins: [ ... new BundleAnalyzerPlugin()) ] };
  70. AGENDA: Let’s create a Webpack build 1. ES6 transpile 2.

    Vendor bundles + Code splitting 3. Preprocessors + Postprocessors 4. Minify JS and CSS + source maps 5. Loading assets: images + fonts 6. Development server 7. File watcher + HML* 8. Environment variables 9. Cache invalidation with hashes 10. i18n* 11. GZIP compression 12. Performance monitoring *Framework dependent