When building a Rails application with considerable JavaScript you might get to a point where the loading order of your JavaScript files matters. Take for example the following code:
js
APP.Car = App.Vehicle.extend(...
In this code we expect to find an object in the App.Vehicle
namespace, so obviously the code that defines App.Vehicle
has to be loaded beforehand. So we will need to be careful with the loading order of our JS files. Because of this you could end up with a JavaScript manifest file that has dozens of entries e.g.
//= require jquery
//= require underscore
//= require_tree app/views
//= require app/models/vehicle
//= require app/models/car
//= require_tree app/controllers
//= require_tree app/helpers
...
Note how we need to manually require vehicle.js
before car.js
to have the code work as expected.
Managing the loading order of your JavaScript files by hand like this is a terrible prospect, especially if you have hundreds of them, so instead of having something like the code above wouldn’t it be much better to have something like this?
//= require jquery
//= require underscore
//= require_tree .
Just load the libraries plus everything in the ‘assets/javascripts’ tree. But that is not going to work as with our previous example (car will be loaded before vehicle).
Namespacing
Namespacing your objects in JS is a good idea, but simply doing this is not going to help at all with the loading order issue.
Angular
Angular has its own module system that makes this a non-issue. So if you are using Angular the load order of files won’t matter.
Require JS
Require JS is great, but using it with the Rails asset pipeline is an unnecessary doubling up of efforts. Both Require.js and the asset pipeline solve the same issues:
- Load all individual files in development
- Provide a way of concatenating files in production
So if you are using the asset pipeline these two problems are already solved for you, Require JS just bring unnecessary complexity, so it is not a good fit.
Enter ModuleJS
We have been using a great library to deal with this problem called ModuleJS. It is a great fit for Rails because it allows us to declare modules and dependencies without adding the extra complexity that comes with Require.js.
ModuleJS doesn’t do asynchronous loading of files like Require.js, it just expects you to load the files by some other means, which is just what we want as the assets pipeline takes care of that part.
For example:
In file app/assets/javascripts/models/vehicle.js
js modulejs.define('models/vehicle', function () {
var Vehicle = ... definition of vehicle model
return Vehicle;});
In file app/assets/javascripts/models/car.js
js modulejs.define('models/car', ['models/vehicle'], function (Vehicle) {
var Car = Vehicle.extend(...);
return Car;});
ModuleJS completely removes the need to worry about the loading order of JS files and it works a treat with the Rails assets pipeline so now our JavaScript manifest can look like this:
//= require jquery
//= require underscore
//= require_tree .
Try it out.