The Rails Asset Pipeline was envisioned before JS module solutions were robust and common. Consequently, it comes with it’s own way to manage dependencies. In the
application.js of any non-trivial app, you will find comments specifying which files to include into this file during deployment.
//= require jquery //= require frameworkjs //= require app
And often, an
app.js that contains further dependencies.
//= require_self //= require storage //= require_tree ./models //= require_tree ./controllers //= require_tree ./views window.App = Framework.createApplication();
When any file changes, itself and all files dependent on it are marked invalid. If
controllers/session.js is changed, three files would become invalid:
app.js does not change if
controllers/session.js changes), Rails will not respond with a 304. It will rebuild all invalid dependencies, even if you are not asking for those dependencies.
In development mode, Rails explodes dependencies into multiple
<script> tags. Each file is linked to and requested separately.
app.js is a dependency of
applications.js, but these files are fetched independently in development mode.
application.js. First a fresh request, then a second one cached.
Started GET "/assets/application.js" for 127.0.0.1 at 2013-06-01 09:54:45 -0400 Compiled controllers/session.js (0ms) (pid 47538) Compiled views/session.js (43ms) (pid 47538) Compiled app.js (50ms) (pid 47538) Compiled application.js (4ms) (pid 47538) Served asset /application.js - 200 OK (77ms) Started GET "/assets/application.js" for 127.0.0.1 at 2013-06-01 09:54:50 -0400 Served asset /application.js - 200 OK (0ms)
77ms is fine. But the same tree with CoffeeScript files:
Started GET "/assets/application.js" for 127.0.0.1 at 2013-06-01 09:58:10 -0400 Compiled controllers/session.js (674ms) (pid 47591) Compiled app.js (604ms) (pid 47591) Compiled application.js (1586ms) (pid 47591) Served asset /application.js - 200 OK (1596ms) Started GET "/assets/application.js" for 127.0.0.1 at 2013-06-01 09:58:15 -0400 Served asset /application.js - 200 OK (0ms)
1.6 seconds! In this example I’m fetching
application.js, but keep in mind that the asset pipeline will recompile all the dependent files when any dependency changes. Even if their generated output will be the same.
application.js.coffee may not have changed, but they will be recompiled nonetheless.
The simplest way to improve performance is to keep your asset dependencies short. There are two ways to do this.
Manage all your dependencies in a single file. By keeping the hierarchy of dependencies shallow, you decrease the number of files that need to be built after a given change.
application.js should look like:
// Libraries // //= require jquery //= require frameworkjs // Application files // //= require ./app //= require ./storage //= require_tree ./models //= require_tree ./controllers //= require_tree ./views
And no other files should contain dependencies. Even if your project uses CoffeeScript, keep
In the development environment, Rails’ default behavior is to explode dependencies into multiple
<script> tags. On larger projects, this overwhelms the browser or crashes the Rails server. If you are disabling this behavior and serving a single file in development, using a shallower dependency tree should improve compilation time significantly.
Alternatively, use a JavaScipt module system. Unfortunately, there isn’t a maintained Asset Pipeline integration of my favored module system, minispade. There is a rails-minispade gem, but it seems to be defunct. https://github.com/jwhitley/requirejs-rails isn’t quite seamless to drop in, making it somewhat difficult to recommend. The es6-module-transpiler is new and interesting, but I see no immediate tools for using it with the asset pipeline.
Given a module system, you should be able to concatenate dependencies in an arbitrary order. This gives the asset pipeline very little work to do.
application.js would look like this:
// Require everything in no particular order // //= require_tree .
However, there isn’t a non-trivial way to do this with the Rails Asset Pipeline today. A better option is to use rake-pipeline and rake-pipeline-web-filters, which are designed with this kind of asset compilation in mind.
Britt Ballard published an excellent post explaining and demonstrating the benefits of module systems beyond better performing asset compilation speeds. Tom Dale’s post AMD is Not the Answer is also good reading on modules.