Managing common tasks
As our collection of React components grows, we'll need ways of bundling them all together. It would also be a good idea for us to minify the resulting JavaScript to reduce the time it takes to load them in a browser.
We can perform these kinds of tasks using scripts in package.json
:
"scripts": { "bundle": "browserify -t babelify main.js -o main.dist.js", "minify": "..." }
NPM scripts are fine for small, simple tasks. When the tasks get more complex, we'll start to see the drawbacks of using NPM scripts for this. There's no easy way to use variables in these scripts, so parameters are often repeated. The scripts are also a bit inflexible and frankly ugly.
There are a few tools that address these problems. We're going to use one of them, called Grunt, to create flexible, repeatable tasks.
The Grunt website has instructions for using Grunt and a list of popular plugins you can use to customize your workflow:
Grunt is a JavaScript task runner. There are three steps for using it:
- First, we need to install the CLI tool. We'll use this to run different tasks.
- Then, we need to install the libraries our tasks will use, via NPM.
- Finally, we need to create a
gruntfile.js
file where we'll put our tasks.
We can install the CLI tool using the following command:
$ npm install -g grunt-cli
Note
The preceding command installs the Grunt CLI tool globally. If you don't want that, omit the -g
flag. You'll need to alias/run it directly with node_modules/.bin/grunt
from here on though.
We will need the following task libraries:
$ npm install --save-dev grunt $ npm install --save-dev grunt-browserify $ npm install --save-dev grunt-contrib-uglify $ npm install --save-dev grunt-contrib-watch
The global CLI tool needs a local copy of grunt
. In addition, we also want the glue libraries to run Browserify, Uglify, and a file watcher in Grunt. We configure them with something like this:
module.exports = function(grunt) { grunt.initConfig({ "browserify": { "main.js": ["main.es5.js"], "options": { "transform": [ "babelify" ], "external": [ "react", "react-dom" ] } }, "uglify": { "main.es5.js": ["main.dist.js"] }, "watch": { "files": ["main.js"], "tasks": ["browserify", "uglify"] } }); grunt.loadNpmTasks("grunt-browserify"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.registerTask("default", ["browserify", "uglify"]); };
We can configure each task in gruntfile.js
. Here, we create a browserify
task, defining the source and destination files. We include the babelify
transformation to convert our ES6 classes into ES5-compatible code.
Note
I've added the external
option so you can see how. If you don't need it, just delete it and your bundle file should then include the full React source code.
After the ES6 code is transformed, we can run Uglify to remove unnecessary whitespace. This reduces the size of the file, so browsers can download it quicker. We can target the file Browserify created and create a new minified file from it.
Finally, we create a watch
task. This watches for changes to main.js
and triggers the Browserify and Uglify tasks. We need to register a default set of tasks, which we set to browserify
and uglify
. This configuration enables the following commands:
$ grunt $ grunt browserify $ grunt uglify $ grunt watch
There are other great tools like Grunt:
They work with similar configuration files, but the configuration is done through functional composition. The important thing to take from this is that there are tools we can use to automate tasks we would have run by hand. They make these repetitive tasks easy!