After Grunt, Gulp, then Browserify, Webpack is the new leading build technology for the frontend. Just as Grunt, it helps you build your codebase, and just as Browserify, it brings support to modularization; but it has more impetus than the others for now. In this post starting with a simple Grunt build, you’ll learn the basics of Webpack and the philosophy behind it.
Comparing Grunt and Webpack
While the use case is quite similar in the two, they are conceptionally different. Grunt is a task runner, while Webpack is a module bundler. Effectively they both take your source files and do a series of transformations to generate some compiled code you can run and deploy.
Also you can integrate Webpack into Grunt, so you don’t need to choose if you don’t want to.
(To learn about other bundlers, click here)
Grunt is a task runner, that is, a utility to run tasks; be them compilation-related or anything else. There are many tasks for Grunt for frontent compilation, but you are free to use it for others purposes. This makes up an easy-to-comprehend architecture; basically there are tasks and configurations. This makes it a very versatile approach.
It’s main concept is fairly simple. You define entry points, and Webpack recursively resolves dependencies; then packs your app into bundles. You can define so called Loaders to define how different types of resources can be loaded (like SCSS needs some preprocessing). That’s basically it.
Some advantages of Webpack over Grunt:
- As a more opinionated tool, it requires simpler configuration while it fits most projects
- Supports modules, even the ES6 syntax out of the box.
- There are many loaders and plugins and a vibrant ecosystem.
- The loaders are like simple UNIX scripts, they can be piped to make more complex transformations easily.
(To learn how to write code that is easier to maintain, click here)
For the sake of example, let’s start with a basic Grunt build and then rewrite it for Webpack!
The original Gruntfile can be found here
I’ll start from the Grunt-based build I introduced in an earlier post. It provides two tasks:
- grunt: Builds the app to the dist directory
- grunt dev: Same as the grunt task, but also watches the src for changes
(To get started with React, click here)
The end result can be found here
As with Grunt, we need to define some dependencies for the build. A notable difference is that Webpack prefers npm dependencies over Bower ones, so the package.json would contain those too. For the devDependencies section, we’ll use the following:
- webpack, webpack-dev-server: Obviously we need Webpack
- style-loader, css-loader: These will load CSS files
- babel-core, babel-preset-es2015, babel-preset-react, babel-loader: These will do the transformations required for React.
- html-webpack-plugin: This will modify the HTML file so that the resulting js will be referenced
The dependencies section contains the runtime dependencies; we’ll only need React for now.
We should define scripts here for building and development:
The first one invokes Webpack. By default, it uses webpack.config.js.
The watch script invokes webpack-dev-server instead of webpack. It is used during development; it does not write anything to the disc. The –hot turns on hot deployment (a page refresh would be needed after every change otherwise), and –inline tells the server to use inline mode, instead of Iframing. Finally, –watch-poll will help if hot loading is not working otherwise, more on this later.
We can run these as npm run build and npm run watch, respectively.
The end result can be found here
Then to the plugins section. The one thing Webpack needs is an HTML file that actually loads it’s generated bundle. The easiest way is to use the HtmlWebpackPlugin, and pass it a template.
The next section is the modules. You can specify filename patterns and you can configure the different kinds of assets in one single place. Also you can pass parameters to the loaders, like the presets for Babel.
In the rules section, we can add a test for the js and jsx files that use the babel-loader. The
options is passed to the loader (this case, Babel) as-is, therefore the latest and react presets will be used, along with the transform-class-properties plugin. Also we can add a loader for the styles, piping the css-loader to the style-loader.
Finally, the devtool: “source-map” specifies that we want source maps too.
Export the component:
Then import and use it whenever needed:
Thanks to the Webpack Hot Loader, if you make a change in any component, the server will update the code and automatically reloads the page. This seriously shortens the time needed to test a change.
Albeit the transition to Webpack went smoother than expected, it comes with it’s own set of problems. These are mostly annoying things that might be fixed eventually and luckily only affect development.
Hot loading vs Vim
If you are using Vim to edit sources, your changes might not be detected by the hot loader unless you use –watch-poll. This is a known issues and caused by how Vim saves files. To make this working, you can configure Vim or simply use –watch-poll.
(To learn more about new Vim features, click here)
Sources in the developer console
The next thing leaves you wondering is where the sources are in the developer console. They reside in the webpack://. folder:
This is a little surprise you’ll eventually get used to.
Debugging with hot reloading
If you use breakpoints, Webpack hot deploy holds some additional surprises for you. You can set breakpoints to your code and they get hit without problems. If you modify something in the code and the modifications are deployed, a new file is created from the modified one. Your already set breakpoints will be useless now, but you can set others in this new file.
However, if you change your code another time, you are out of luck. You can not set additional breakpoints. You need to do a full reload to regain this capability; a quirk I hope will soon be fixed.
Even if you manage to stop the code at a breakpoint, it is still hard to evaluate anything in the console. Even though there is an import ReactDOM from “react-dom”; line earlier and the Component properly showing up, typing ReactDOM to the console gives an Uncaught ReferenceError: ReactDOM is not defined. After a bit of trial and error, you could figure out it’s current name, like _reactDom2, but it makes copy-pasting carefully crafted snippets back to the codebase quite hard.
On the bright side, it can be tackled on the browser-side. There is an issue in the Chromium tracker, and the necessary proposals are handled in to make this happen. Seems like it won’t happen anytime soon, but it’s nice to see things are progressing.