Introducing Webpack

When writing a Node.js application, the last thing we want to do is to manually add support for a module system different from the one offered as default by the platform. The ideal situation would be to continue writing our modules as we have always done, using require() and module.exports, and then use a tool to transform our code into a bundle that can easily run in the browser. Luckily, this problem has already been solved by many projects, among which Webpack (https://webpack.github.io) is one of the most popular and broadly adopted.

Webpack allows us to write modules using the Node.js module conventions, and then, thanks to a compilation step, it creates a bundle (a single JavaScript file) that contains all the dependencies our modules need for working in the browser (including an abstraction of the require() function). This bundle can then be easily included into a web page and executed inside a browser. Webpack recursively scans our sources and looks for references of the require() function, resolving and then including the referenced modules into the bundle.

Tip

Webpack is not the only tool we have for creating browser bundles from Node.js modules. Other popular alternatives are Browserify (http://browserify.org), RollupJs (http://rollupjs.org) and Webmake (https://npmjs.org/package/webmake). In addition, require.js allows us to create modules for both the client and Node.js but it uses AMD in place of CommonJS (http://requirejs.org/docs/node.html).

Exploring the magic of Webpack

To quickly demonstrate how this magic works, let's see how umdModule, we created in the previous section looks, if we use Webpack. First, we need to install Webpack itself; we can do so with a simple command:

npm install webpack -g

The -g option will tell npm to install Webpack globally so that we can access it using a simple command from the console, as we will see in a moment.

Next, let's create a fresh project and let's try to build a module equivalent to the umdModule we created before. This is how it looks if we had to implement it in Node.js (file sayHello.js):

var mustache = require('mustache'); 
var template = '<h1>Hello <i>{{name}}</i></h1>'; 
mustache.parse(template); 
module.exports.sayHello = function(toWhom) { 
  return mustache.render(template, {name: toWhom}); 
}; 

Definitely simpler than applying a UMD pattern, isn't it? Now, let's create a file called main.js, that is, the entry point of our browser code:

window.addEventListener('load', function(){ 
  var sayHello = require('./sayHello').sayHello; 
  var hello = sayHello(Browser!'); 
  var body = document.getElementsByTagName("body")[0]; 
  body.innerHTML = hello; 
}); 

In the preceding code, we require the sayHello module in exactly the same way as we would do in Node.js so, no more annoyances for managing dependencies or configuring paths; a simple require() does the job.

Next, let's make sure to have mustache installed in the project:

npm install mustache

Now comes the magical step. In a terminal, let's run the following command:

webpack main.js bundle.js

The previous command will compile the main module and bundle all the required dependencies into a single file called bundle.js, which is now ready to be used in the browser!

To quickly test this assumption, let's create an HTML page called magic.html that contains the following code:

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

This is enough for running our code in the browser. Try to open the page and see it with your eyes. Boom!

Tip

During development, we don't want to manually run Webpack at every change we make to our sources. What we want instead is an automatic mechanism to regenerate the bundle when our sources change. To do that, we can use the --watch option when running the Webpack command. This option will keep Webpack running continuously and it will take care to re-compile our bundle every time one of the related source files changes.

The advantages of using Webpack

The magic of Webpack doesn't stop here. This is a (incomplete) list of features that make sharing code with the browser a simpler and seamless experience:

  • Webpack automatically provides a version for many of the Node.js core modules that are compatible with the browser. This means that we can use modules such as http, assert, or events, and many more, in the browser!

Tip

The fs module is among those not supported.

  • If we have a module that is incompatible with the browser, we can exclude it from the build or replace it with an empty object or with another module providing an alternative and browser-compatible implementation. This is a crucial feature and we will have the chance to use it in the example we are going to see shortly.
  • Webpack can generate bundles for different modules.
  • Webpack allows us to perform additional processing of the source files using third-party loaders and plugins. There are loaders and plugins for almost everything one might need, from CoffeeScript, TypeScript, or ES2015 compilation, to support for loading AMD, Bower (http://bower.io), and Component (http://component.github.io) packages using require(), from minification to the compilation and bundling of other assets such as templates and stylesheets.
  • We can easily invoke Webpack from task managers such as Gulp (https://npmjs.com/package/gulp-webpack) and Grunt (https://npmjs.org/package/grunt-webpack).
  • Webpack allows you to manage and pre-process all your project's resources, not just JavaScript files but also stylesheets, images, fonts, and templates.
  • We can also configure Webpack to split the dependency tree and organize it into different chunks that can be loaded on demand whenever the browser needs them.

The power and flexibility of Webpack are so captivating that many developers started to use it even to manage client-side only code. This is also made possible by the fact that many client-side libraries are starting to support CommonJS and npm by default, opening new and interesting scenarios. For example, we can install jQuery as follows:

npm install jquery

And then, we can load it into our code with a simple line of code:

const $ = require('jquery'); 

You will be surprised at how many client-side libraries already support CommonJS and Webpack.

Using ES2015 with Webpack

As we said in the previous paragraph, one of the main advantages of Webpack is the ability to use loaders and plugins to transform the source code before bundling it.

Throughout this book we have been using many of the new handy features offered by the ES2015 standard, and we would love to keep using it even when working on a universal JavaScript application. In this section, we are going to see how to leverage the loader feature of Webpack to re-write the previous example using the ES2015 syntax within our source modules. With the proper configuration, Webpack will take care to transpile the resulting code for the browser to ES5 to guarantee the maximum compatibility with all the currently available browsers.

First of all, let's move our modules to a new src folder. This will make it easier for us to organize our code and separate the transpiled code from the original source code. This separation will also make it easier for us to configure Webpack properly and simplify the way we invoke Webpack from the command line.

Now we are ready to rewrite our modules. The ES2015 of our src/sayHello.js will look like this:

const mustache = require('mustache'); 
const template = '<h1>Hello <i>{{name}}</i></h1>'; 
mustache.parse(template); 
module.exports.sayHello = toWhom => { 
  return mustache.render(template, {name: toWhom}); 
}; 

Notice that we are using const, let, and the arrow function syntax.

We can now update our src/main.js file to ES2015. Our src/main.js file instead can be re-written as follows:

window.addEventListener('load', () => { 
  const sayHello = require('./sayHello').sayHello; 
  const hello = sayHello('Browser!'); 
  const body = document.getElementsByTagName("body")[0]; 
  body.innerHTML = hello; 
}); 

Now we are ready to define the webpack.config.js file:

const path = require('path'); 
 
module.exports = { 
  entry:  path.join(__dirname, "src", "main.js"), 
  output: { 
    path: path.join(__dirname, "dist"), 
    filename: "bundle.js" 
  }, 
  module: { 
    loaders: [ 
      { 
        test: path.join(__dirname, "src"), 
        loader: 'babel-loader', 
        query: { 
          presets: ['es2015'] 
        } 
      } 
    ] 
  } 
}; 

This file is a module that exports a configuration object that will be read by Webpack when we invoke it from the command line without any argument.

In the configuration object, we are defining the entry point as our src/main.js file and the destination for our bundle file as dist/bundle.js.

This part was quite self-explanatory, so let's now have a look at the loaders array. This optional array allows us to specify a set of loaders that can alter the content of our source files while Webpack constructs our bundle file. The idea is that every loader represents a specific transformation (in this case, ES2015 to ES5 using babel-loader) and it is applied only if the current source file matches the specific test expression defined for the loader. In this example, we are telling Webpack to use babel-loader on all the files coming from our src folder and to apply the es2015 preset as Babel option.

Now we are almost ready; the only missing step before running Webpack is to install Babel and the ES2015 preset with the following command:

npm install babel-core babel-loader babel-preset-es2015

Now, to generate your bundle, you can simply run:

webpack

Remember to reference the new dist/bundle.js in your magic.html file. You should be able to open it in the browser and see that everything is still working properly.

If you are curious, you can read the content of the freshly generated bundle file and you will discover that all the ES2015 features we used in the source files have been converted to the equivalent code valid in ES5, which every browser on the market can execute just fine.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.72.15