Some time later, you realize you need to clean up your script block, but you have no idea which ones are still being used and which aren't. You remove some tags you think are unneeded and the site seems fine when you click around, but later you get a bug report about a broken widget that was actually using that library.
It doesn't have to be this way
It's time to embrace modularity in our client-side code. Instead of writing tightly integrated code that depends on everything being in the global scope, we should strive to create decoupled, discrete components that clearly define their dependencies and the functionality that they export. There are many tools to help with this, but two of the most popular are Browserify and RequireJS.
Browserify and transforms
Though we used RequireJS briefly, in the end we chose Browserify for its simplicity, easy extension through transforms, and its focus on npm and the Node.js module system. It is an astoundingly flexible system that implements a "require" function on the browser and cleanly encapsulates modules so that they don't pollute the global scope.
Transforms allow Browserify to become incredibly versatile. You can consume AMD packages with deAMDify, or use browserify-shim to consume libraries that attach themselves to the window. You can take advantage of alternate package management systems, like Bower with debowerify or Component with decomponentify. You can smoothly handle code that needs to be precompiled, like CoffeeScript and JSX. You can even precompile things like Handlebars templates so that you can "require" them in your modules.
Let's get to work!
So, enough talk about why. Let's move on to how! Browserify is built with Node.js, so you will need to have it installed on your system. To take your first steps with Browserify, you'll probably want to install it globally so that you can use it as a command-line script.
$ npm install -g browserify
Now, let's write a simple module that requires something:
There are a few things you'll notice here. First, some of you will immediately point out that I'm using
'use strict' outside of a function, and chastise me because that will apply strict mode to the entire global scope and break all the things! Thankfully, that's not the case here. Browserify encapsulates every module in it's own scope, so strict mode will only apply to the current module.
To use the Underscore library, I'm calling "require" and assigning it to the familiar "_" variable. At the moment, however, this will fail because we haven't installed it yet. Remedy this with npm:
$ npm install underscore
By calling the "install" command without the "-g" flag, you're telling npm to install your dependency in the local "node_modules" folder, which it will create for you if needed. Browserify will use that folder to find Underscore and make it available to your module.
Finally, at the end of the module I'm "exporting" the function that I defined. This means that I am making that function available outside of my module. When another module requires my module, the result will be whatever I assigned to "module.exports". This is how Node.js modules work. Anything I don't export stays private to my module.
Building a bundle
Now, let's use the command-line script to build a bundle for the browser. This will include all the required modules in one file. If you saved your module above as "logunderscore.js", browserify it like this:
$ browserify logunderscore.js > bundle.js
Requiring your own modules
When you need to require one of your own modules, use the relative path. You don't need to add the ".js" at the end of the path.
Exporting multiple things
If you need to export multiple functions or objects, you can use the "exports" shortcut from Node.js.
Then, you can use it like this:
Or like this:
Integrating Browserify with build tools
Once you're comfortable with Browserify, you'll probably want to integrate it with your favorite build tool.
Browserify and Grunt
In Grunt, you'll use grunt-browserify. Here's a config snippet that builds the bundle, and then watches for changes:
This config takes advantage of several features, some of which I haven't mentioned yet. It's using the reactify transform to precompile JSX files for use with React. It instructs browserify to look for ".jsx" extensions so that you don't have to include them in your require path. It sets the debug flag so that Browserify will generate source maps for effective debugging in development, but overrides that flag in the production target to keep the build lean.
The "alias" option makes a reqirement available through a global "require" function. This allows you to work with multiple bundles, if you'd like. Here, though, it's being done so that the React dev tools extension can find React and enable a tab in Chrome. The "alias" setting in the Grunt plugin uses the
bundle.require() method from Browserify's API, which is also available with the "-r" flag on the command-line script.
Browserify and Gulp
The gulp-browserify plugin is currently a bit more minimal than its Grunt counterpart, but you can still do everything that you'd like to do by listening for the "prebundle" event and interacting with the bundler API directly.
You, too, can Browserify today!
Hopefully this guide has illustrated the usefulness of Browserify and helped you get it up and running yourself. If you've got questions or comments, let me know below or find me on Twitter @bkonkle. Happy coding!