Webpack - The module bundler | A sweeping introduction

With increasing popularity of Single Page Applications (SPAs), the modern websites have become dramatically more dynamic and interactive. Consequentially, they've gone far ahead in complexity as well, bringing in challenges for web developers to keep up the performance of the app. Now if you're a backend guy, things are a little easy. You've got hundreds of tools to manage your development process. But on front end, things become tedious and tough to deal with, pretty fast.

In this article, we'll get around Webpack. The build tool has already gained tons of buzz in the community, specially with ReactJS playing around. We've also seen people getting into debates or being confused about what to use when we have industry popular Grunt and Gulp around. We'll talk in brief about that as well in a while.

Let's see what we'll be dealing with in this article.

  1. What's Webpack ?
  2. Webpack against Grunt or Gulp
  3. Setup a simplest possible Webpack configuration so that you can take leverage of this beast at your best.

What is Webpack?

webpack will stop once the module , the module that deals with a dependency, and generates a static asset from that module.

-Webpack page Official Description

Webpack is a module bundler that packs your NodeJS modules to be run on the browser. You must be wondering how's it different from Browserify ? Well, yes, both the tools Webpack and Browserify do bundle the modules in order to support CommonJS or AMD, but they differ much in their approach and technique. If you want to delve deep into Webpack Vs Browserify topic, you can read Browserify Vs Webpack JS-Drama.

Webpack is a very different tool altogether. It's versatile, and has been made for NodeJS, from ground up. Let's see what Webpack can provide us to make our development process easy on front end:

  1. A module resolver for our entire source code.
  2. Hot Module Replacement. So you can see your code changes reflecting in the browser, without getting into the refresh thing every time you make a change.
  3. Bundle Splitting. You can bundle your modules in various ways. Webpack helps you split your bundles that may load dynamically when your application needs them. Pretty Cool!
  4. Asset Hashing. Webpack allows you to easily add hashes to your bundle names. That makes effective client-side invalidation.

And countless other. It's totally customisable.
I guess by now, you must have got a rough idea of Webpack and it's distinctive features. Before diving into some code, let's see what's the confusion between Webpack, Grunt and Gulp.

Webpack against Grunt or Gulp

Shall we use Webpack?

Shall we stick to the much reliable and stable tool Grunt, or maybe Gulp?

Will Webpack make our build process easy?

Which tool is better - Grunt, Gulp or Webpack?

Everyone's using Webpack these days. Shall we be using it, too?

Wait, wait, wait, wait..And Relax. These are common doubts that hit the minds of developers new to Webpack. Or NodeJS itself. Well, the answer to these questions is pretty easy. There's no competition at all. Grunt and Gulp are task runners. Whereas, Webpack is a module bundler such as Browserify. Although Webpack can perform the tasks done by Grunt/Gulp with a close efficiency, at the end of the day, it's a super efficient module bundler and must be made to do the same work.

There has to be no confusion regarding what to chose among Webpack, Grunt or Gulp. Grunt/Gulp are task runners. Whereas, Webpack is a super powerful module bundler.

Choosing a tool is a decision based on your need.

Let's try to understand things a little closely.

Grunt

Grunt Logo

Grunt is the first ever JS task runner that became the mainstream. Much before Gulp came in. The popularity credit for Grunt goes to its plugin style configuration. However, it has few negatives as well, as all other things.

  1. Its plugins may get overly complex to use once you're with it on a large project that needs a number of tasks running to get things done.
  2. Grunt performs tasks hiding away the under the hood functioning. Consequently, when your codebase grows and you need to add some custom tasks, things get a bit problematic.
  3. Grunt has no concept of piping as Gulp. It implements a write-read-write strategy while playing with files. Not very smart! Right?

But here comes the good thing. You can use grunt-webpack plugin and let webpack do the heavy lifting.

Gulp

Gulp Logo

Gulp is an extremely powerful task runner. With it's unique API that implements streaming (piping of processes), things are easier to configure, build and run. Here you deal with the code itself and Gulp hides nothing. It's transparent and so it's easy to figure out solutions whenever you go stuck into something.

Similar to Grunt scenario, Webpack is available to you here as well as gulp-webpack.

Let's move to Webpack.

Webpack

Webpack Logo

Webpack, a build tool, comes with a powerful core that you can extend using loaders and plugins. Loaders and plugins are just a way to add custom functionality to Webpack.

But as we discussed earlier, choosing amongst Webpack and Grunt/Gulp is a decision based on your needs. You can always develop your own Webpack loaders and plugins to match your development needs. It's much easy and effective.

Hello, Webpack!

Let's get started with setting up a simplest possible structure of our app so we can start writing webpack configurations.

Let's create a directory for the demo app.

/
-- dist/
  |-- bundle.js    // our webpack bundle
-- src/
  |-- index.js
-- style/
  |-- style.scss
index.html  
webpack.config.js  
package.json  

We have a structure. So now we need to create a directory and move into it.

$ mkdir webpack-demo && cd webpack-demo

Firstly, we need to create out template file that'll load our assets.

<html>  
<head>  
  <script src="./dist/bundle.js"></script>
  <title>Webpack Demo</title>
</head>  
<body>

</body>  
</html>  

To use the webpack, we need to install it.

$ npm install -g webpack

Now that we've got webpack onboard, let's write a simple configuration file that'll be used by webpack. Good news, it's just vanilla JS.

var config = {  
  entry: './src',
  output: {
    path: './dist',
    filename: 'bundle.js'
  }
};

module.exports = config;  

Great! We've written our configuration that webpack can use. We can now add a script to our package.json that'll be used to run webpack.

// package.json

//...

"scripts": {
  "build": "webpack"
}

What's happening here? Well, now whenever we run the command npm run build, webpack will look for webpack.config.js and build our node modules for the browser environment.

Cool! We just did the minimalist setup for our demo app. Now it's time to create the entry point for our webpack, that is ./src/index.js.

// index.js

console.log("Hello, Webpack!");  

Go! Build the script!

$ npm run build

You can now open index.html in your favorite browser to see Hello, Webpack! printed in console.

Find it there? Awesome. You just used Webpack for building up your assets.

Now that you can use webpack for building up your assets, let's see what else can we make it do for us.

Transpiling ES6 to JS

Similar to browserify transforms, we're provided with loaders in webpack. A loader's role is to teach webpack how to deal with different file syntax/formats.

Here we'll tell our webpack configuration to load our ES6 code and transpile it to JavaScript before bundling the modules.

Firstly, we need to install loaders. In our case we'll be using a Babel loader.

$ npm install --save-dev babel-loader

We need to install few additional dependancies like babel-preset-es2015. You can read more about their configuration on babel-loader installation instructions.

$ npm install --save-dev babel-core babel-preset-es2015

var config = {  
  // entry and output configs..
  // ,

  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015']
        }
      }
    ]
  }
};

module.exports = config;  

Now you're all set to write ES6 code that will work well in the browsers, too! Moving on.

Stylesheets

Let's put our ./style/style.scss file to use. We probably need to do some webpack configuration with some new loaders that can help webpack understand the SASS/CSS files. The webpack can then bundle them along with our JS code.

Install the new loaders.

$ npm install --save-dev css-loader style-loader sass-loader

Herein, we're installing three types of loaders. The css-loader and style-loader together teach the webpack to handle CSS files. But since we're using SASS for our writing our styles, we need sass-loader to help us understand webpack what to do. Simple, right? Cool. Let's write some configuration code.

var config = {  
  // entry and output configs..
  // ,

  module: {
    loaders: [
      // our babel-loader config here..
      // ,
      {
        test: /\.scss$/,
        loader: 'style!css!sass'
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      }
    ]
  }
};

module.exports = config;  

Didn't find something right in place? Don't worry, we're just getting there. So what the heck is style!css!sass? Well, a good news again. Webpack supports piping of loaders. So we can use all three loaders we just installed simply with separation of !.

But wait, we must be using some images in our styles, right? Well, since they're altogether a different file format, we need to introduce some loader that could understand that. Moving on.

Images

At the core, our images are just URLs. So all we need is a loader that can understand and load the files. url-loader come to the rescue.

$ npm install --save-dev url-loader

Umm, let's push it to the configuration.

var config = {  
  // entry and output configs..
  // ,

  module: {
    loaders: [
      // our other loader configs here..
      // ,
      {
        test: /\.(png|jpg)$/,
        loader: 'url-loader'
      }
    ]
  }
};

module.exports = config;  

Awesome. We're all set with the configuration for styles. Oh, wait! Fonts? Well, those are files too. Aren't they? Just use the url-loader for them as well.

Let's include our styles file in the script so that webpack can build the styles, too.

// index.js

require("./../style/style.scss");

console.log("Hello, Webpack!");  

Globals

Backend has always been gifted with the global properties and variables that they can access from anywhere in their code. Webpack brings that magic power to the front end as well. Let's see how can we do that.

Defining the globals, we'll make use of webpack's second big helper - plugins. We'll be using webpack's native definePlugin to do the job. Let's set global process.env.NODE_ENV.

var config = {  
  // entry, output and module configs..
  // ,

  plugins: [
    new webpack.DefinePlugin({
      "process.env": {
        NODE_ENV: JSON.stringify("production")
      }
    })
  ]
};

module.exports = config;  

So we just set the global environment variable process.env.NODE_ENV with a value as production. Similarly, you can easily configure n number of globals as per your need and convenience.

Congratulations, you just created a working webpack configuration for building your new node app.

You can also learn how Instagram is using webpack at production.

Hope you loved your first experience with webpack. In the next post, we'll learn some advanced usage of webpack that can help you leverage the power of this awesome tool at development, as well as production.

Till then, Happy coding!