Monday, April 01, 2013

Angry Birds of JavaScript: Yellow Bird - RequireJS

Introduction


A diabolical herd of pigs stole all of the front-end architecture from an innocent flock of birds and now they want it back!

A team of special agent hero birds will attack those despicable pigs until they recover what is rightfully theirs, front-end JavaScript architecture!

Will the birds be successful in the end? Will they defeat their bacon flavored foe? Let's find out together in another nail biting episode of Angry Birds of JavaScript!

Check out the series introduction post for a list of all the birds and their attack powers.

Previous Attacks



Yellow Bird Attacks


In this post we will take a look at the Yellow Bird who comes with a RequireJS speed booster and dynamically injects scripts against those pesky swine. Slowly, one by one, the birds will take back what it theirs to keep!

What Was Stolen by the Pigs?


The birds used to manually add script tags to their HTML files. At first this wasn't an issue, but when their application started to grow larger and more complex it started to become difficult for them to organize their code, figure out dependencies, and determine a strategy for optimizing performance. Thankfully they were introduced to the RequireJS library which provided them a way to manage their code into modules, load their scripts asynchronously, manage their dependencies, and provide an easy way to optimize. Unfortunately the pigs, during a recent invasion, stole the RequireJS library from the birds.

One of the yellow birds has been tasked to reclaim what has been stolen. He will use the optimization power of speed to help destroy the pigs in order to take back what is theirs.

Broken Application


Let's first start with a simple little web page that contains just a few scripts. You'll notice that I'm loading 3 popular libraries (jQuery, Underscore, and Postal) and some custom code at the end.


The above code looks pretty straightforward, but when I end up running the page I get the following error in the dev tool's console...


Fictitious Internal Dialog: "WHAT!?! I don't see any each method anywhere. What's up with that? Ohh man, it looks like the exception occurred in postal.min.js somewhere. FOUND A BUG... see if I use that library again. But, wait!?! Ohh, maybe something else is going on here."

So, the real issue isn't a bug in postal.js, the issue is in that postal.js has a dependency on underscore.js. The problem is that underscore should have been loaded before postal.js. Simply rearranging the script tags could easily solve this issue. In the above case the fix was trivial, but imagine how cumbersome this could be once the project starts to get large and requires lots of scripts.

RequireJS Basics


Before we go and look at how we could fix the above situation using RequireJS, let's first take a high level overview of what the library is doing for us. RequireJS is an Asynchronous Module Loader and the API it provides allows us to define and require modules. Both functions are really easy to understand so let's take a look at them.

define method


In order to create a module you need a name, a list of dependencies, and a callback function.


require method


At some point in your application you will need to use the require function to kick things off.


Fixed Application


Using RequireJS I took the above little application and rearranged some things. You should notice that the following markup removes all the script tags except one, which points to the require.js library. RequireJS knows where to start because we add an HTML5 data-main attribute describing where the main script is located.


The main script has a configuration section inside of it where you can assign aliases to existing AMD modules and also shim libraries that were not previously defined. Although jQuery and Postal define themselves as AMD modules we included them in our configuration because they are not located alongside main.js.

You don't have to include in your config any custom modules you define in your application. You can refer to those by their file path and name.


Optimize


Our application only has 5 script files in it, but as you know our app will only continue to add additional scripts. So, it would be nice if there was an easy way to combine and minify our scripts for better production performance. The nice thing is that by using RequireJS we have already defined the dependencies of our application.

Thankfully there is a tool called r.js that takes this dependency information and uses it to generate a combined and minified script. You can install the tool with the Node Package Manager npm install requirejs

You could provide all the command line argument to the tool in the console, but I prefer making a build config file like the following to define all of its settings before I run it. You can find a comprehensive list of settings from the official GitHub repository.


Once you've defined your build.js file then you let r.js know you want to use it. The following command will get you going... r.js -o build.js. You can see the output of the tool in the output below.


Additional Resources


I only scratched the surface on all the things you can do with RequireJS and the r.js optimization tool. If you are interesting in learning more about these concepts you may want to look at some of the following resources.


Attack!


The following is a simple Angry Birds clone using boxbox, a framework for the box2dweb JavaScript physics library, written by Bocoup's Greg Smith.

Press the space bar to launch the Yellow Bird and you can also use the arrow keys.


Conclusion


Front-end web applications can get complicated quickly. It is nice to have a way to provide some structure, dependency management, and an easy way to optimize the final result. Thanks to the power of Yellow the birds have regained their trusty RequireJS tool for use in their next application.

There are many other front-end architecture techniques that have been stolen by the pigs. Tune in next time as the next Angry Bird takes its revenge! Dun, dun, daaaaaaa!

No comments:

Post a Comment