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

Get to Know Webpack

Get to Know Webpack

Webpack is a JavaScript module bundler that allows you to organize your code, make it modular, and create more maintainable applications. It has become one of the most popular tools for modern web development. But, do you know that webpack allows you to import other asset types as modules beside JS, including CSS, HTML, images and fonts? In addition, webpack has features available to actually help you with web performance. In this session, we'll look at several of the different features of webpack, see how they work and learn how you can use them in your applications.

Jonathan Kemp

October 17, 2019
Tweet

More Decks by Jonathan Kemp

Other Decks in Programming

Transcript

  1. Starter kits exist so that we can start writing the

    code without having to understand the tools.
  2. When everything works out of the box, you might not

    know what's really happening under the hood, which is important to understand at some level.
  3. Getting to know Webpack • Why webpack • Getting started

    • Asset management • Output management • Development vs. production • Optimize site performance
  4. Script Loading <script src="//typekit.com/fj3j1j2.js"></script> <!-- This second script won’t execute

    until typekit has executed, or timed out --> <script src="//my.site/script.js"></script>
  5. Problems with Script Loading • The browser downloads in parallel,

    maintains order of scripts • Script #2 won’t execute until the previous script has executed • The browser blocks further rendering of the page until this is finished
  6. This is why you put <script> elements at the end

    of your document, as it blocks as little content as possible!
  7. Problems with Script Loading Shared global scope: • Any variables

    declared in one of these files or inline scripts would be added to the global window object. • Can lead to collisions and/or broken experiences.
  8. JavaScript Modules • Closures (IIFE) • RequireJS (AMD module format)

    • Node.js and CommonJS modules (npm) • Every file is a module with its own scope and context.
  9. Browserify • A tool for compiling CommonJS modules for the

    browser. • Lets you require('modules') in the browser by bundling up all of your dependencies. • Bridged the gap between CommonJS modules (npm) and the browser
  10. ECMAScript (ES6) Modules • The ES6 specification included a module

    system native to JavaScript • Every file is a module with its own scope and context.
  11. Getting Started • At its core, webpack is a static

    module bundler for modern JavaScript applications. • Basically processing javascript modules and their dependencies and combining them into one file.
  12. Getting Started • When webpack processes your application, it internally

    builds a dependency graph which maps every module your project needs and generates one or more bundles. • Any time one file depends on another, webpack treats this as a dependency.
  13. This allows webpack to take non-code assets, such as images

    or web fonts, and output them as dependencies.
  14. When webpack processes your application, it starts from a list

    of modules defined on the command line or in its config file.
  15. Entry Points • Starting from these entry points, webpack recursively

    builds a dependency graph that includes every module your application needs, • Then bundles all of those modules into a small number of bundles to be loaded by the browser.
  16. src/index.js import _ from ‘lodash'; function component() { let element

    = document.createElement('div'); element.innerHTML = _.join(['Hello', 'webpack'], ' '); return element; } document.body.appendChild(component());
  17. Run webpack • With that said, let's run npx webpack,

    which will take our script at src/index.js as the entry point, and will generate dist/main.js as the output. • The npx command, which ships with Node 8.2/npm 5.2.0 or higher, runs the webpack binary (./node_modules/.bin/webpack)
  18. Modules • The import and export statements have been standardized

    in ES2015. • Although they are not supported in all browsers yet, webpack does support them out of the box. • Behind the scenes, webpack actually "transpiles" the code so that older browsers can also run it. • Note that webpack will not alter any code other than import and export statements. If you are using other ES2015 features, make sure to use a transpiler such as Babel or Bublé via webpack's loader system.
  19. Run webpack • Let's run npx webpack • This will

    take our script at src/index.js as the entry point • And will generate dist/main.js as the output.
  20. Most projects will need a more complex setup, which is

    why webpack supports a configuration file.
  21. webpack.config.js const path = require('path'); module.exports = { entry: ‘./src/index.js',

    output: { path: path.resolve(__dirname, 'dist'), filename: ‘main.js' } };
  22. Did you know? • https://generatewebpackconfig.netlify.com is an interactive portal you

    can play around by selecting custom webpack configuration options tailored for your frontend project. • https://createapp.dev/webpack is an online configuration tool for creating webpack config where you can select any combination of features you need. • Starter kits, of course
  23. Loaders • Out of the box, webpack only understands JavaScript

    and JSON files. • Loaders allow webpack to process other types of files and convert them into valid modules that can be consumed by your application and added to the dependency graph. • • This means that the same benefits can be applied to everything used in building a website or web app.
  24. Loaders • Files • JSON • Transpiling • Templating •

    Styling • Linting and Testing • Frameworks
  25. Transpiling • babel-loader - Loads ES2015+ code and transpiles to

    ES5 using Babel • Traceur, TypeScript and others
  26. Loading CSS • In order to import a CSS file

    from within a JavaScript module, you need to install and add the style-loader and css-loader to your module configuration
  27. Loading Images • Using the file-loader we can easily incorporate

    backgrounds and icons in our system as well, as well as importing images in modules. • The html-loader handles img tags in the same manner. • A logical next step from here is minifying and optimizing your images. • Check out the image-webpack-loader and url-loader for more on how you can enhance your image loading process.
  28. Loading Fonts • The file and url loaders will take

    any file you load through them and output it to your build directory. • This means we can use them for any kind of file, including fonts.
  29. Output Management • As your application grows and once you

    start using hashes in filenames and outputting multiple bundles, it’s difficult to keep managing your index.html file manually. • Plugins exist that will make this process much easier to manage.
  30. Plugins • While loaders are used to transform certain types

    of modules, plugins can be leveraged to perform a wider range of tasks. • bundle optimization, e.g. minification • asset management, e.g. cleaning up the /dist folder • injection of environment variables
  31. Multiple Entry Points • We'll be adding our src/print.js as

    a new entry point. • We'll change the output as well, so that it will dynamically generate bundle names, based on the entry point names.
  32. Multiple Entry Points const path = require('path'); module.exports = {

    entry: { app: './src/index.js', print: './src/print.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
  33. Multiple Entry Points • But what would happen if we

    changed the name of one of our entry points, or even added a new one? • The generated bundles would be renamed on a build, but our index.html file would still reference the old names. • Let's fix that with the HtmlWebpackPlugin.
  34. Setting up HtmlWebpackPlugin • HtmlWebpackPlugin by default will generate its

    own index.html file, even though we already have one in the dist/ folder. • This means that it will replace our index.html file with a newly generated one. • If you open index.html in your code editor, you'll see that the HtmlWebpackPlugin has created an entirely new file for you and that all the bundles are automatically added.
  35. Cleaning up the /dist folder • Webpack will generate the

    files and put them in the /dist folder for you, but it doesn't keep track of which files are actually in use by your project. • In general it's good practice to clean the /dist folder before each build, so that only used files will be generated. • A popular plugin to manage this is the clean-webpack-plugin so let's install and configure it.
  36. CleanWebpackPlugin const { CleanWebpackPlugin } = require('clean-webpack-plugin'); plugins: [ new

    CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Output Management' }) ],
  37. Modes • When not in production some libraries may add

    additional logging and testing to make debugging easier • However, production mode they might drop or add significant portions of code to optimize how things run for your actual users.
  38. Choosing a Development Tool • It quickly becomes a hassle

    to manually run npm run build every time you want to compile your code. • There are a couple of different options available in webpack that help you automatically compile your code whenever it changes 1. webpack's Watch Mode 2. webpack-dev-server 3. webpack-dev-middleware
  39. Using Watch Mode "scripts": { "test": "echo \"Error: no test

    specified\" && exit 1", "watch": "webpack --watch", "build": "webpack" },
  40. Using Watch Mode • webpack can "watch" all files within

    your dependency graph for changes. • If one of these files is updated, the code will be recompiled so you don't have to run the full build manually. • The only downside is that you have to refresh your browser in order to see the changes.
  41. Using webpack-dev-server module.exports = { mode: 'development', entry: { app:

    './src/index.js', print: './src/print.js' }, devtool: 'inline-source-map', devServer: { contentBase: './dist' },
  42. Using webpack-dev-server • webpack-dev-server provides you with a simple web

    server and the ability to use live reloading. • This tells webpack-dev-server to serve the files from the dist directory on localhost:8080
  43. Using webpack-dev-server "scripts": { "test": "echo \"Error: no test specified\"

    && exit 1", "watch": "webpack --watch", "start": "webpack-dev-server --open", "build": "webpack" },
  44. Hot Module Replacement • Hot Module Replacement (HMR) exchanges, adds,

    or removes modules while an application is running, without a full reload. • This can significantly speed up development in a few ways: 1. Retain application state which is lost during a full reload. 2. Save valuable development time by only updating what's changed.
  45. Enabling HMR const webpack = require('webpack'); module.exports = { devServer:

    { contentBase: './dist', hot: true }, plugins: [ new webpack.HotModuleReplacementPlugin() ],
  46. Development mode • Source maps • Development tools • Watch

    mode • webpack-dev-server • Hot Module Replacement (HMR)
  47. Development goals • Make debugging easier • Automatically rebuild the

    bundle and reload the page when a change is made
  48. Browser Compatibility • webpack supports all browsers that are ES5-compliant

    (IE8 and below are not supported) • @babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s) • Transpile only what is not supported in your browsers matrix.
  49. babel.config.js const presets = [ [ "@babel/env", { targets: {

    edge: "17", ... }, }, ], ]; module.exports = { presets };
  50. Tree Shaking • A term commonly used in the JavaScript

    context for dead-code elimination. • Can yield a significant decrease in bundle size when working on larger applications with complex dependency trees. • The webpack 2 release came with built-in support for ES2015 modules (alias harmony modules) as well as unused module export detection. • When you import modules, some of these modules have unused exports that should be dropped.
  51. src/math.js export function square(x) { return x * x; }

    export function cube(x) { return x * x * x; }
  52. webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.js',

    path: path.resolve(__dirname, 'dist') }, mode: 'development', optimization: { usedExports: true } };
  53. Tree Shaking • The square function is what's known as

    "dead code", meaning an unused export that should be dropped. • We've cued up our "dead code" to be dropped by using the import and export syntax, but we still need to drop it from the bundle. • To do that, set the mode configuration option to production.
  54. webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.js',

    path: path.resolve(__dirname, 'dist') }, mode: 'production' };
  55. Code Splitting • This feature allows you to split your

    code into various bundles which can then be loaded on demand or in parallel. • It can be used to achieve smaller bundles and control resource load prioritization which, if used correctly, can have a major impact on load time.
  56. General Approaches to Code Splitting • Entry Points: Manually split

    code using entry configuration. • Prevent Duplication: Use the SplitChunksPlugin to dedupe and split chunks. • Dynamic Imports: Split code via inline function calls within modules.
  57. Entry Points module.exports = { mode: 'development', entry: { index:

    './src/index.js', another: './src/another-module.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
  58. Entry Points • If there are any duplicated modules between

    entry chunks they will be included in both bundles. • It isn't as flexible and can't be used to dynamically split code with the core application logic.
  59. Prevent Duplication • The SplitChunksPlugin allows us to extract common

    dependencies into an existing entry chunk or an entirely new chunk. • With the optimization.splitChunks configuration option in place, we should now see the duplicate dependency removed from our index.bundle.js and another.bundle.js.
  60. Dynamic Imports module.exports = { mode: 'development', entry: { index:

    './src/index.js' }, output: { filename: ‘[name].bundle.js', chunkFilename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
  61. Dynamic Imports • Now, instead of statically importing lodash, we'll

    use dynamic importing to separate a chunk • Calls to import() are treated as split points, meaning the requested module and its children are split out into a separate chunk. • Note the use of webpackChunkName in the comment. This will cause our separate bundle to be named lodash.bundle.js instead of just [id].bundle.js.
  62. Lazy Loading • Lazy, or "on demand", loading is a

    great way to optimize your site or application. • This practice essentially involves splitting your code at logical breakpoints, and then loading it once the user has done something that requires, or will require, a new block of code. • This speeds up the initial load of the application and lightens its overall weight as some blocks may never even be loaded.
  63. Dynamic Imports • The code here “lazy-loads" it as soon

    as the script is run. • The trouble is that no user interaction is required to load the bundle -- meaning that every time the page is loaded, the request will fire.
  64. Lazy Loading button.onclick = e => import(/* webpackChunkName: "print" */

    ‘./print') .then(module => { var print = module.default; print(); });
  65. Frameworks • Many frameworks and libraries have their own recommendations

    on how this should be accomplished within their methodologies. • Here are a few examples: React, Vue, Angular
  66. Conclusion • It's a tool that lets you bundle your

    JavaScript applications and it can be extended to support many different assets such as images, fonts and stylesheets. • Since version 4.0.0, webpack does not require a configuration file to bundle your project, nevertheless it is incredibly configurable to better fit your needs. • Loaders allow webpack to process other types of files and convert them into valid modules that can be added to the dependency graph.
  67. Conclusion • While loaders are used to transform certain types

    of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables. • With modes, we can take advantage of different features for development vs. production • webpack cares about site performance and load times and gives you the tools to improve
  68. Resources • awesome-webpack - A curated list of awesome Webpack

    loaders, plugins, resources, libraries and tools: https://github.com/webpack-contrib/ awesome-webpack • Working examples from webpack: https://github.com/webpack/ webpack/tree/master/examples