Webpack Basics
Tags: webpack, javascript
Published: 2017-06-23 15:54:52

---
title: Webpack Basics
published: true
description: Using Webpack for a simple application.
tags: webpack, javascript
---

_TL;DR I created a repository with the example [here](https://github.com/kay-is/webpack-example/tree/master)_ :)

Many people seem to like [Webpack](https://webpack.js.org/) and use it for their everyday web bundling process, but I heard from many others that they simple can't comprehend how to use it in the first place. So I had the idea of creating a (very) basic project and write about parts of the config, just HTML, CSS and JS, nothing fancy.

# Installation

For this example project I used the following dependencies:

    "devDependencies": {
      "css-loader": "^0.28.4",
      "style-loader": "^0.18.2",
      "html-webpack-plugin": "^2.28.0",
      "http-server": "^0.10.0",
      "webpack": "^3.0.0"
    }

Two loader modules to be able to load CSS via JS.

One Webpack plugin, that lets me create HTML files that will automatically have script tags for the created bundles.

A HTTP server, that simply serves static files.

And finally, Webpack itself.

While Webpack and http-server are _global_ Node modules - they are run via the command line - you can install them locally in the `devDependencies`, they will be pseudo-global accessible for npm scripts.

    "scripts": {
      "setup": "npm i && npm run build && npm start",
      "build": "webpack",
      "start": "http-server -c-1 ./build"
    },

`npm run build` is just an alias for `webpack`, but it works without installing Webpack globally. Same goes for `npm start`, which is just an alias for the http-server call.

# Basic Config Structure

The Webpack config file, often named `webpack.config.js`, is just a JS file which gets executed inside [Node.js](https://nodejs.org/en/). It has to export a configuration object.

What this means first and foremost is, that you can basically use all your Node modules in it and write JavaScript as you are used to. This gives you much flexibility for the creation of the config object.

A basic config file could look like this:

<pre><code>
const path = require("path");
const HtmlPlugin = require("html-webpack-plugin");

const html = new HtmlPlugin({ template: "./modules/index.html" });
const outputPath = path.resolve(__dirname, "build");

module.exports = {
  entry: {
    application: "./modules/index.js"
  },

  output: {
    filename: "[name].[chunkhash].js",
    path: outputPath
  },

  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },

  plugins: [html]
};

</code></pre>

## Imports and Pre-Config

    const path = require("path");
    const HtmlPlugin = require("html-webpack-plugin");

    const html = new HtmlPlugin({ template: "./modules/index.html" });
    const outputPath = path.resolve(__dirname, "build");

First you import the modules you need for the creation of the config object.

Here I import the Node path module and the [Webpack HTML plugin](https://github.com/jantimon/html-webpack-plugin).

Next you write the things that need to be done before the config object can be created. You can do this in-line if you like, I just prefer it like this. Often you have many plugins and this can get unwieldy, if they're all created inline.

In the example I create an instance of the HTML plugin and get the absolute path to my output directory. 

## Creating the Config Object

The next part is the creation of the config object. It has 4 important sections: `entry`, `output`, `module` and `plugins`.

### Entry

    entry: {
      application: "./modules/index.js"
    },

The `entry` tells Webpack where to start. For this you have to understand that Webpack works with a tree structure. It takes one or more entries and looks in these entry files if some other files are imported, this goes down to til no file is importing another anymore.

If nothing different was configured elsewhere, Webpack will create one bundle-file for every entry, only one in this example.

Another reason for more than one bundle-files are dynamic imports. If you use `import("my-module").then(myModule => ...)` instead of `import myModule from "my-module"` somewhere, Webpack will automatically create additional files, that are imported when `import` is called.

### Output

    output: {
      filename: "[name].[chunkhash].js",
      path: outputPath
    },

Here we configure the names of the bundle files. You can use a _fixed_ name, or some placeholders. I used `[name]` and `[chunkhash]`.

`[name]` will be replaced with either a key from the `entry` object, or with a dynamically generated ID. For example if you used dynamic imports, they will be named automatically by Webpack.

`[chunkhash]` will be replaced with a hash, that reflects the content of this bundle. Which means it changes every time you changed a file that went into this. This forces every new version into a new bundle file, which helps when you want your users to only download the latest version. In dev mode I often just use `[name]` so I won't end up with hundreds of bundles.

The `path` has to be a absolute path to your output directory, so I generated it with the help of Nodes `path` module on run-time.

### Module

    module: {
      rules: [
        {
          test: /\.css$/,
          use: ["style-loader", "css-loader"]
        }
      ]
    },

Here the modules to are defined. Often they just consist of a bunch of rules, which are associated with loaders. 

When I import a CSS file, please run it through the `style-loader` and `css-loader`.

_Webpack only knows about JavaScript_, so you have to tell it what else you need. Often the loaders implement some mechanisms to embed non-JavaScript assets into JavaScript, or make them accessible from JS.

This can also be used with Babel or TypeScript, so your ES2015, or `.ts` files are compiled down to ES5 or something. It even works with LESS, SASS, images, fonts, etc. pp.

It all works on a _per file_ basis, which means, the loader only looks at _one file_ at a time and tries to convert it somehow.


### Plugins

    plugins: [html]

Plugins can work on multiple files. This allows things like extracting all CSS texts from the bundles and putting them into a separate CSS file or to create a HTML file, that will include all the created JavaScript bundles.

In the example I used `[chunkhash]` as part of the bundle file names, which leads to different file names every time I build with changed code.

The HTML plugin allows me to create a HTML file from a template of my liking and fills it with script tags for the bundle files. So every time I run Webpack, I get a new `index.html` that already includes the right file names.

# Non-Config Factors

Like I mentioned above, the config is not the only thing that influences your output. 

If you're using dynamic imports, this leads to implicit code splitting.

In my example, I used 4 pages. Home, about, contact and loading. Home and loading are imported _statically_, about and contact _dynamically_.

Webpack can infer from the `import("./pages/" + variable)` that the dynamic pages are all inside of `./pages` so it creates bundles for all files in that directory, but without the ones that are already imported _statically_.

When you access the example on a web-server, you see that the 2 dynamic bundles are only loaded after a link was clicked. Other intermediate bundles Webpack also created as part of its build process are not loaded by the browser.

# Remarks

You can run Webpack with `webpack --watch`, which will trigger a compilation every time you change a file. Webpack even has its own dev-server, which can be used to improve the dev process even further, with hot module replacements on the fly etc.

You can have multiple config files and run them via `webpack --config <name>` or create them based one some environment variables. Some libraries run in development mode by default and require you to do things differently when compiling for production, so often you won't get away with one _simple_ config file.

# Conclusion

Webpack is a powerful tool and often hard to grasp, especially if you're just learning JavaScript and suddenly are forced to get the Webpack concepts in your head, but when looking closer, there aren't many of them and even dynamic code splitting and loading is often nicely handled without explicit configuration.