Monday, 8 June 2015

Test-Driven React with Karma and Webpack: #3 Webpack Setup

So, we started off by outlining building a small application with modern JavaScript tools, and then built a small module for Conway's Game of Life. This was test-driven using Karma. Now, we are going to introduce Webpack and React into the mix.

In this article, I'll set the stage for creating the front-end component for the game. We'll introduce Webpack and react-hot-loader to ease the development of the project and build an HTML-only React component for our simple UI.

Installing Webpack and react-hot-loader

Webpack is kind of a multi-purpose bundler for JavaScript applications.  It has tons of great features (like support for all type of module systems etc.) and you should definitely check out the project website to read the details. I'm going to assume that you know what the tool does and just focus on the details of installation.  The configuration is going to be quite basic, but I'll also introduce react-hot-loader so that the project live reloads in the browser.  This is going to help make the development work easy because we won't have to continually refresh to see changes.

Ok, since we already have npm set up, the first thing we'll do is pull in some new packages:

$ npm install webpack --save-dev
$ npm install react-hot-loader --save-dev 
$ npm install babel-loader --save-dev
$ npm install webpack-dev-server --save-dev

Installing the packages give us everything we need to progress with the project.

The babel-loader could be replaced with jsx-loader, but the Babel project allows us to also use some ES6 (ES2015) features if we want to in the future.  Later, we might integrate Babel with Karma, but at the moment, there is no need.  Babel supports the transpilation of our React jsx syntax and that is what we are after for this project.

The react-hot-loader works with the webpack-dev-server to create a web socket connection that it can use to live reload.  So, we need the webpack-dev-server project to support those requirements. react-hot-loader holds a dependency on React, so there is no need to install it separately.

At this point, Webpack isn't going to do much.  It requires a simple config file so that it knows what directories and files to watch and where to deliver them.  So, we'll create a new file named webpack.config.js and add it the root directory.  Inside the file will look like this:

There are a few areas of the file that are interesting. The entry section shows that we are building up the runtime of the app by passing through the webpack-dev-server.  Finally, we give index.js as the entry point for the application.  We'll create that file in a minute.

The next section to look at is the output section.  This section shows were the files will be delivered in order to sever them.  The /assets/ directory (in our case) will only be virtually served through the development server, so there is no need to create this directory.

Finally, the module section is interesting because it shows the loader pipeline that the code will go through.  First, the react-hot loader will watch for file changes.  Then, the babel-loader will transpile the files in order to serve them up.  The test property gives a regular expression indicating that we are interested in .js files for this pipeline.  For this project, we won't use the .jsx extension.  We'll just use .js and Babel will take care of the rest.

At the moment, running the webpack command from the root directory will complain with the following message:

Module not found: Error: Cannot resolve 'file' or 'directory' ./index.js in /home/life/src

So, let's create the index.js file in the /src directory. At the moment just create a completely empty file /src/index.js.  Now, go back to the root directory and run the following command:

$ webpack

This time you should see a successful build.  We didn't add any code, but if you look in the /dist/bundle.js file, you can see that a lot of dependencies have been included.  So, we are now set up to configure the hot loading capability.
As a side node.  The index.js file acts as an entry point for the application.  It is important to separate the entry point file from the files that make up your components so that the hot loader can live reload the files that change.  If you put everything inside the main entry point file, the react-hot-loader will complain that any change requires a complete refresh.
In order to configure hot loading, we need a dev server.  The dev server runs as an express app, so the easiest thing is to add a server.js file the the root directory with the following contents:

This is taken from the awesome react-hot-boilerplate project by gaearon.  You can see that in the file we reference the Webpack config publicPath.  This was the /assets/ directory mentioned earlier.

After adding the server.js file, we'll add an npm script in the package.json file to start up the server:

The scripts property is a top-level property of the package.json file. Here we define a start command that will run our previously defined server.js file with node.

Run the following command to build and run the dev server:

$ npm start

You should see a server start on port 3000 (or your configured port) and then the bundle.js file created and served.

We still aren't serving anything of interest, so let's add an index.html file in the root directory and link in our /assets/bundle.js.  So create a file named index.html in the root directory with the following contents:

Inside the above file we set up a div with the id set to content.  This will be our application root for the React component.

We still are not going to be able to see the react-hot-loader in action.  This is because only dependencies of the entry point file index.js will be involved in the live reload.  In order to show that working, let's begin to create the skeleton of the React application.

A Simple React Application with react-hot-loader

In order to create an flat React application, let's add a new directory named components inside the /src directory. Then, inside the new /src/components directory, add a new file named game-of-life.js.  Add the following contents to the new file:

In the above file, we create a div with the text "Game of Life." inside and render that in a React component. It is important to remember to export the component so that we can require this as a dependency in the index.js file.

We also have to make a slight addition to the /src/game.js file in order to require it in the above file.  We must export the Game function so that game-of-life.js can require it.  We'll do this by passing in module or window and binding the function returned in the module:

The defensive check to see if (typeof module !== 'undefined') is important because this code also has to run under the Karma tests.  And we have not set up Karma to understand modules.

Finally, let's require the game-of-life.js dependency by adding this to index.js:

The code above requires the game-of-life module and then binds it to the content div in index.html.

Now, if we run the command npm start, the dev server will start up.  Open a browser and visit http://localhost:3000. You should see the text "Game of Life." rendered to the screen.  Now the fun part.  Open up the /src/game-of-life.js file in your favourite text editor and change "Game of Life." to "Conways Game of Life.".  After saving the file, you should see the value automatically update in the browser!

We are now all set up with Webpack and react-hot-loader.  In the next article, we'll start to build the component for Conway's Game of Life.  You can check out the completed repository now if you want.

No comments:

Post a Comment