DEV Community

Vishang
Vishang

Posted on

Webpack for React (intro)

What is webpack?

Webpack, at its core, is a code bundler. It takes your code, transforms and bundles it, then returns a brand new version of your code.

What problem it's solving?

Think about how many times we have to take our code and change it so it's compliant with what the browser is used to (vanilla HTML, CSS, and JavaScript). If you've ever used a CSS Preprocessor like SASS or LESS you know you need to transform your SASS/LESS code into normal CSS.

Webpack really shines is you're able to tell it every transformation your code needs to make, and it will do them and output a bundle file for you full of those changes (and some other helpful things as well like minification if you desire).

Three things webpack should know

  • webpack needs to know the starting point of your application, or your root JavaScript file.
  • webpack needs to know which transformations to make on your code.
  • webpack needs to know to which location it should save the new transformed code.

The first thing we need to do is create a file which is going to contain our webpack configurations. Conveniently, this file should be named webpack.config.js and be located in the root directory of our project.

Now that we have our file made we need to make sure that this file exports an object which is going to represent our configurations for webpack.

// In webpack.config.js
module.exports = {}
</code></pre>

First tell our webpack an entry point in our app
<pre><code>
module.exports = {
    entry: './app/index.js',
}
Enter fullscreen mode Exit fullscreen mode

All we do is give our object a property of entry and a value which is a string which points to our root JavaScript file in our app.

Now that we've told webpack where to start, we need to tell it which transformations to actually make. This is where loaders will come in handy.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
}
Enter fullscreen mode Exit fullscreen mode

it tells webpack to run the coffee-loader on all extensions that end in .coffee.

last step is specifying where webpack should output the new transformed code.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  }
}
Enter fullscreen mode Exit fullscreen mode

The code here is pretty self explanatory. filename is the name of the file that webpack is going to create which contains our new transformed code. path is the specific directory where the new filename (index_bundle.js) is going to be placed

So now when webpack runs, our code will be transformed and then can be referenced at ourApp/dist/index_bundle.js.

Challenge after doing all this

if you see our minial folder structure after doing all this is

    /app
        components
        utils
      index.js
      index.html
    /dist
      index.html
      index_bundle.js
    package.json
    webpack.config.js
    .gitignore
Enter fullscreen mode Exit fullscreen mode

So as you can see, our code we're developing with is found in the app folder and our transformed code is in the dist folder. Now you can visually see the issue. We want to change the index.html located in the app folder but the index.html file that the browser is actually going to be using is located in the dist folder (because that's where we've also told webpack to spit out the transformed JS file).

One extra step

Instead of actually copying our index.html file, it's just going to use that file as a template and create a brand new index.html file. This plugin is the html-webpack-plugin. As always, you'll need to run npm install --save-dev html-webpack-plugin before you can use it. Now we just need to tell webpack what we want to do with it.

First thing, we'll need to create a new instance of HTMLWebpackPlugin and specify one thing, the template of what we want the newly created file to look like.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ]
}
Enter fullscreen mode Exit fullscreen mode

Now if we run webpack from our command line, inside of our dist folder we'll have two files. index_bundle.js and index.html. index_bundle.js is the result of taking our entry code and running it through our loaders. While index.html was created on the fly with HTMLWebpackPluginConfig and is a copy of our original index.html file located in our app folder with a script tag referencing the newly created index_bundle.js file

Now let's take a look at our index.js file after webpack ran
app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="app"></div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My App</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="app"></div>
  <script src="index_bundle.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

You'll notice that the only difference between the two files is that the one in dist (which was created with HTMLWebpackPlugin) now has a script tag pointing to index_bundle.js . Again, the only real magic going on here is that HTMLWebpackConfig is smart enough to detect the output filename of your newly created file from Webpack and it will automatically add that as a script in your newly created index.html file. So in our example we used index_bundle.js as the output filename so as you can see in the created index.html file above, we have now have inside the body. If we were to change the output of our webpack config to be OUR-AWESOME-JS-FILE.js, then inside the body of our newly create index.html file we would have

Finally, as of Webpack 4, back in our webpack.config.js file we need to tell it which "mode" we want it to run in - "production" or "development". For now, we'll just set the mode to "development". This will enable things like tooling for debugging and faster builds.

module.exports = {
  entry: './app/index.js',
  module: {
    rules: [
      { test: /\.coffee$/, use: "coffee-loader" }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'app/index.html'
    })
  ],
  mode: "development"
}
Enter fullscreen mode Exit fullscreen mode

In order to do that, you'll need to install two things. "webpack" and "webpack-cli". Once installed, you'll be able to add a new script to your package.json which runs webpack.

"scripts": {
  "build": "webpack"
},
Enter fullscreen mode Exit fullscreen mode

Now in your terminal you can run "npm run build" which will do a one time run through of your settings then compile your code and output into a dist folder. However, this can be kind of a pain to keep having to run the command over and over whenever you change anything. To fix this, change webpack in your NPM script to run webpack -w and that will watch your files and re-execute webpack whenever any of the files Webpack is concerned about changes. Lastly, if you're wanting to ship to production, you can run webpack -p and that will run through the normal transformations as well as minify your code.

Top comments (2)

Collapse
 
donghopark profile image
Dongho Park

Helpful! Have read your other articles too. Thanks!

Collapse
 
tcarmine profile image
Carmine

nice article, there is unfortunately something unclear as under the app/index.html index.html should be place as template otherwise webpack does not resolve and does not know what to use