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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var webpack = require('webpack'); | |
module.exports = { | |
context: __dirname + "/src", | |
entry: { | |
app: ['webpack-dev-server/client?http://localhost:3000', | |
'webpack/hot/only-dev-server', | |
'./index.js' | |
] | |
}, | |
output: { | |
path: __dirname + "/dist", | |
filename: "bundle.js", | |
publicPath: "/assets/" | |
}, | |
plugins: [ | |
new webpack.HotModuleReplacementPlugin(), | |
new webpack.NoErrorsPlugin() | |
], | |
module: { | |
loaders: [ | |
{ test: /\.js$/, loaders: ["react-hot", "babel-loader"], include: __dirname + "/src" }, | |
] | |
} | |
} |
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var webpack = require('webpack'); | |
var WebpackDevServer = require('webpack-dev-server'); | |
var config = require('./webpack.config'); | |
new WebpackDevServer(webpack(config), { | |
publicPath: config.output.publicPath, | |
hot: true, | |
historyApiFallback: true | |
}).listen(3000, 'localhost', function (err, result) { | |
if (err) { | |
console.log(err); | |
} | |
console.log('Listening at localhost:3000'); | |
}); |
After adding the server.js file, we'll add an npm script in the package.json file to start up the server:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//... | |
"scripts": { | |
"start": "node server.js" | |
}, | |
//... |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Conway's game of life with ReactJS</title> | |
<link rel="stylesheet" href="/styles/style.css"> | |
</head> | |
<body> | |
<div id="content"></div> | |
<script src="/assets/bundle.js"></script> | |
</body> | |
</html> |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require('react'), | |
Game = require("../game/game.js"); | |
var GameOfLife = React.createClass({displayName: 'GameOfLife', | |
render: function() { | |
return ( | |
<div className="main"> | |
Game of Life. | |
</div> | |
); | |
} | |
}); | |
module.exports = GameOfLife; |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Game = (function (module) { | |
//...some code here | |
return module.exports = function (size) { | |
init(size); | |
return { | |
grid: grid, | |
setCell: setCell, | |
nextFrame: nextFrame | |
}; | |
}; | |
})((typeof module !== 'undefined') | |
? module | |
: window); |
Finally, let's require the game-of-life.js dependency by adding this to index.js:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require('react'), | |
GameOfLife = require('./components/game-of-life'); | |
React.render( | |
<GameOfLife />, | |
document.getElementById('content') | |
); |
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