Quick Tip: Write Modular JavaScript With Browserify
The browserify tool has been around for a few years already, but I didn't really take notice until I started developing with node.js. This is when I appreciated the agility and power that come with writing modular JavaScript.
Browserify can be used in any web app, regardless of the server side language, and in this quick tip I will show you how you can use it to modularize your code, while demonstrating some of the benefits that come with it.
First, what is a module?
The basic idea behind a module, is that it is an isolated and arbitrarily complex piece of code. It may or may not include other modules, and it explicitly exports objects or functions to the code that includes it. This is better than simply splitting your source into separate js files, and including them in script tags, because modules let you reuse code in ways which are not possible otherwise, do not pollute your global scope, and are easier to test. See more benefits here.
JavaScript doesn't (yet) have a native way of defining modules, so the community stepped in and invented several ways to do it. The two most popular are the CommonJS specification, which is used by node.js and browserify, and AMD which is used by asynchronous libraries like require.js.
In this article, we will discuss CommonJS and browserify.
What is browserify?
Web browsers are unable to load CommonJS modules directly, because it would require the entire page to freeze while a module is downloaded, which would be a horrible experience for users. Browserify is a utility that lets you write your JavaScript as if modules are supported by the browser, and when it comes time to deploy, it compiles your code together with the modules you've used into a single js file that you include as a script in your page.
With browserify you can easily modularize your apps, and which is even better - it lets you use all those wonderful libraries written for node.js in much the same way.
Installing it
To use browserify, you need to have node.js and npm installed. I have written some instructions here. Then you need to install browserify, and optionally minify, so that you can cut the size of your js files. Write these commands in your terminal/command prompt:
npm install -g browserify minify
This will fetch the packages, and will set up the browserify and minify commands for you.
A quick example
Let's write a small modular app, which loads a JSON file with movies via AJAX, and lets users purchase movie tickets by writing commands in their browser's dev tools. To demonstrate how Browserify works with libraries, I will include the superagent AJAX library.
To install superagent, type this command in your terminal:
npm install superagent
This will download the library files and all other dependent packages. Lots of client-side libraries are available on npm, including jQuery. So in addition to the other benefits that browserify gives you, it will also be much easier to fetch and download libraries from the net.
We are now ready to write some actual code! The first file that we will write is a module that defines the common methods that the users of our cinema app will use.
assets/js/api.js
module.exports = function(global, movies){ var tickets = []; global.showMovies = function(){ movies.forEach(function(m){ console.log(m.id + ') ' + m.name + ' | $' + m.price.toFixed(2)); }); }; global.buyTicket = function(id){ id = id || 0; id -= 1; if(movies[id] !== undefined){ tickets.push(id); console.log('You bought a ticket for "' + movies[id].name + '"!') } else{ console.error('No such movie!'); } }; global.showTickets = function(){ tickets.forEach(function(id){ console.log('Ticket for "' + movies[id].name + '" | $' + movies[id].price.toFixed(2)); }); }; global.totalCost = function(){ var total = 0; tickets.forEach(function(id){ total += movies[id].price; }); console.log('You have to pay $' + total.toFixed(2)); }; }
This module exports a function, which takes an object and an array of movies as arguments. As you will see in our main program, we will fetch the array via ajax, and the global object will be set to window
. I have chosen not to refer to window
directly in this module, because receiving it as an argument will make it easier to reuse the code in other situation (or on the server side, if we are using node.js).
Here is the main file:
assets/js/main.js
// Require the superagent library var request = require('superagent'); // Require our own module var api = require('./api.js'); // Send a GET AJAX request request('assets/movies.json', function(res){ if(res.ok){ // Initialize the API api(window, res.body.movies); } else{ throw new Error('An AJAX error occured: ' + res.text); } });
Because browsers do not support modules natively, we need browserify to convert this code into a single file for us. This can be done with the following commands:
browserify assets/js/main.js > assets/js/include.js minify assets/js/include.js assets/js/include.min.js
The first command will compile your main.js
file to include.js
. The latter file will contain your code together with all modules you use. The second command minifies it (removes whitespace, comments, and renames variables) to cut the file size of the script and to make it faster to download.
I have provided a shell script - build.sh, which you can execute under Unix/Linux/OSX with this command:
bash build.sh
If you are running Windows, you could probably create a batch file with the same contents and execute it from your command prompt.
All that is left, is to add the include.min.js
file to your page:
from index.html
<script src="assets/js/include.min.js"></script>
You can see the movies.json
file and the other resources in the downloadable zip file, linked from the button above.
We're done!
This concludes today's quick tip. I hope that you found our little experiment useful and that you'll begin your adventure in the land of modular JavaScript!
Bootstrap Studio
The revolutionary web design tool for creating responsive websites and apps.
Learn more
Great post, but there is anyway to make browserify watch the files that it compiling?.
To make it automatically recompile your files it would be best to use a system like grunt or gulp. There are other libraries and modules available in Node that can do it for you, but it would involve writing some code.
You can use watchify to automatically recompile javascript files when there are changes. Additionally, check out parcelify for css / template bundles.
grunt-watchify is a wrapper around browserify
Isn't the whole point of modules to avoid polluting the global scope? I find it strange that you use your custom module to attach functions to a global object. Surely it'd be much better to attach those methods to a return object.