How script loaders work in general
The theory is that script loaders, when loading in multiple scripts through the use of script tags, block the page loading; a page can only render itself once all the JS scripts have been loaded. This can be partially negated by minifying all your scripts into one file but even so, it will still block the page rendering. Hence, script loaders that can load in scripts both asynchronously and in parallel (multiple files concurrently) offer a distinct advantage. Even if you’re combining and minifying your JS into one file, including it through a script loader will often perform better than including it traditionally through a script tag.
Most script loaders follow a very similar pattern in their usage. The first step is to pass in file names to be loaded:
Because the scripts are being loaded asynchronously, you cannot simply run any code that relies on those scripts when you like. Usually we include code than depends on our scripts below the script tags that load the libraries required; when loading asynchronously this won’t work, as you cannot guarantee your dependencies will be loaded when your code is executed. The solution all script loaders provide is a method that will be invoked once the scripts have loaded. These accept a function which will execute when all scripts are loaded.
Now we’ve covered the basics of a generic script loader, let’s take a look at five I’ve picked out and the pros and cons of each.
The first is HeadJS, created by Tero Piirainen. HeadJS was one of the most popular script loaders but has not been updated in recent months, with the last commit on the Github repository being ten months ago. Despite this, it remains an excellent script loader that is worth examining. Basic usage of HeadJS is very straight forward:
If you’d rather not pass the code in as a callback to the loading function, you can use head.js() to load in the files and then pass in your code to head.ready:
One of the best features of HeadJS is the ability to label your scripts, and run code once certain ones have loaded. For example, let’s say you’re loading in two scripts, jQuery and Google Analytics, but your code only relies on jQuery, so you want to execute it as soon as jQuery is loaded. HeadJS offers a really nice way of doing this:
If you don’t want to label the files, you can pass in head.ready() the file name and it achieves the same thing:
That presumes your main JS file is titled “app.js” and is in the same directory as the file that script tag is in. If your JS was in a sub directory of “scripts”, you would give RequireJS this path:
Note how you don’t have to add “.js” onto the end of that, RequireJS has that covered for you.
Now, within the main file (in my case, app.js) we can load in scripts by using the require() function. The require() function takes two arguments, the scripts to load and the function to execute once those scripts have loaded. Keep in mind that the script paths are relative to the path of app.js:
That’s basic usage of RequireJS, but it’s actually capable of much more. RequireJS implements the AMD (asynchronous module definition) spec, which is a method for defining modules which can be loaded. You write all your code as modules, mainly through RequireJS’s define() function. This is a great approach when you have a large application with multiple scripts, all depending on certain others. Whilst this is far too much to go into in this article (I could write multiple articles purely on RequireJS), Jim Hoskins has written a great post on creating and using your own modules with RequireJS that I highly recommend.
yepnope.js is a script loader with a slightly different objective than HeadJS or RequireJS. yepnope is designed to be a resource loader that can load in scripts conditionally, based on any statement you can evaluate to either true or false. It’s designed for loading in polyfills. How often have you had to write some extra JS because the browser doesn’t support something? It’s something that happens pretty often, especially if you’re using brand new APIs and features that not all browsers have implemented yet. It fits perfectly with a feature detection library like Modernizr – so much so that the Modernizr source actually contains yepnope within it.
Using yepnope is easy. We use the yepnope() function, passing in an object which looks something like this:
Here I load in a polyfill for HTML5 placeholder attributes if it’s not natively supported. Of course, we are not limited to just Modernizr tests. What if we are adding a class to the html tag of “.ie” if the browser is IE8 or less, and want to load in files based on that?
It’s also easy to load a script with no conditional through “load” and also perform a test that relies on that script:
It’s worth noting that yepnope is not necessarily the quickest script loader, that’s not its aim, but it does offer conditional loading that can’t be matched by any other resource out there.
For example, let’s say we have jQuery and a plugin to load. With LABjs we’d do it like so:
Rather than pass in an array of files, we call .script() for every file we want to load in. LABjs also signifies dependencies differently. Here .wait() means to wait for jQuery to load, before loading in the plugin. This means you can be confident jQuery is loaded before the plugin is. If you want to run some code once everything is loaded, pass a function into wait():
You can read more on LABjs usage in the documentation. However it’s important to note that as I was writing this article, it was announced that there will be no more development on LABjs. Whilst this is a shame, it remains a reliable script loader (and the code is on Github too if you want to maintain it or fix bugs) and I felt it worthwhile of including in this article.
LazyLoad loads files in parallel, not in order, so when you need to load a script but only once another one has, the best approach is:
The functionality is very similar for loading in CSS, but it’s done through the .css() function.
I hope this article has served to demonstrate some popular libraries and how best to use them. Different libraries do different things better and you should always look to pick the best for the situation in hand. Personally, anything more than a couple of simple scripts, I tend to look towards RequireJS, which is packed full of functionality. If I’m working with polyfills, it’s yepnope every time and for something simple I usually go for HeadJS. However, that’s not to say those choices would suit you, so I encourage you to try as many as you can to get a good knowledge of them and be able to pick out the best one for the situation in hand.