Removing 'require_tree .' from application.js

I posted an issue about this ( https://github.com/rails/rails/issues/17457#issuecomment-61292491 ) but I’ll copy/paste here for discussion convenience:

I feel like I’m the boy telling the emperor he has no clothes, but here goes…

Every large rails project that I’ve worked on has had it’s application javascript grow and grow because everybody leaves ‘require_tree .’ in application.js. Personally I hate it and remove it whenever I can (it’s harder on legacy projects), but we should be loading javascript in a saner, more modular way.

Very often I automagically include javascript files for controllers and actions if they exist. Here’s an example of a helper that I’ve recently written (it’s not neccessarily pretty, but you get the idea:

def action_and_controller_javascript_include_tag
    capture do
      concat javascript_include_tag controller_name if Rails.root.join("app/assets/javascripts/#{controller_name}.coffee"      ).exist?
concat javascript_include_tag "#{controller_name}/#{action_name}" if Rails.root.join("app/assets/javascripts/#{controller_name}/#{action_name}.coffee"    ).exist?
end
  end

I’d be fine if that were a gem, though rails already generates controller js files so it might make sense still.

But seriously, can we please encourage not loading all of the javascript for the entire application?

Brian,

I think require_tree often leads to problems (it’s one of the first things I remove when I’m building an app), but by loading specific JS files per page, aren’t you denying your users’ browsers the opportunity to cache the JS from other page visits? Separate per-page JavaScript files means your user has to download new script assets each time they visit a page related to a controller they haven’t visited before.

-Geoff

For sure, but it would be a relatively small file, wouldn’t it? There’s a lot of (reasonable) concern about making multiple HTTP requests, but that’s usually when you’re evaluating the performance of a single page. As you go between pages you’d be just loading what you need.

Honestly it might require some benchmarking of various apps to compare javascript rendering performance vs HTTP request speed. Also that varies a lot from browser to browser and network to network. Maybe somebody who’s been through this before can speak up? :wink:

Also memory usage and performance due to lots of event handlers should be considered as your application’s set of javascript files grows.

Brian

;p

Brian-

You raise some great points and are moving into some bigger picture stuff about how one actually writes Javascript.

In general (although there are some exceptions), having one large concatenated JS file, properly GZIPed, pushed to an asset bucket (like Amazon), pulled from a CDN (like Cloundfront), is the best way to go. Yes, you can split your manifests into different sub-sections, and load only “what you need” on any given page, but for most apps – even large apps – you just get batter overall performance with one JS file.

Sometimes as a middle-ground I will create different manifests (and load them separately) for different areas of my website – like one for the user-facing and a different one for the admin-facing sections. That can be a good middle of the road approach. Or, if you have a public facing site that has one set of JS and a dashboard for only a subset of users, you could have different manifests for each.

I think that if every page and every controller loaded a different JS, you’d simply be slowing down the user’s experience by making all those files load as the user moved through the website.

Some of it has to do with scale, are we talking about a 20 KB javascript or 300+ KB of javascript? At the 25-50 KB range, I wouldn’t even worry about it. At a bloated 300+ KB of Javascript scale, splitting up stuff might make more sense.

Let me put it this way: You’re going to get so much more of a speed increase from properly gzipping and moving your assets to a CDN, that your strategy of splitting up your website into separate JS is like saying we should upgrade the gears on our bicycle when the right thing to do is replace the bicycle with a car.

For sure, but it would be a relatively small file, wouldn’t it? There’s a lot of (reasonable) concern about making multiple HTTP requests, but that’s usually when you’re evaluating the performance of a single page. As you go between pages you’d be just loading what you need.

Well no, actually, the way it is supposed to work is that only the first page loads the needed JS, and all other subsequent page loads will use the same JS file from cache, so you really are speeding up the user’s load time on all subsequent page visits after the first one.

Honestly it might require some benchmarking of various apps to compare javascript rendering performance vs HTTP request speed. Also that varies a lot from browser to browser and network to network. Maybe somebody who’s been through this before can speak up? :wink:

Also memory usage and performance due to lots of event handlers should be considered as your application’s set of javascript files grows.

Here’s where you raise a much bigger can of worms. In my opinion, lots of JS that I see is garbage. In particular, I think the Rails community has yet to really grapple with the limitations and inefficiencies of “unobtrusive style” javascript, specifically excessive use of DOM-targetted jQuery. (By “DOM-targetting jQuery,” I mean you have lots of jQuery that targets generic class names, attaching click handlers or event functions.) I see this in an old-style Rails-Javascript apps that really haven’t evolved much since 2007.

The pain you are describing is real, and you are not alone. At small scale, that style of writing Javascript works OK. For medium or large size apps, you quickly find yourself in DOM-targeted nightmare: Your page slows down because of so many attachments being made, events being fired, etc. You use classes you think are safe but some JS somewhere attaches click handlers to them producing unexpected results. You get double-attachment errors (when two attachments are made to the same object, causing the function to fire twice). You use Ajax to rerender part of your page and you drown in having to re-apply attachments.

In my mind, this style of writing Javascript is a disaster (I would even call it an antipattern), and it’s really unfortunate that more of the Rails community hasn’t seen the light and realized that outside of the Rails world most professional JS developers moved on from this kind of writing JS back in 2009-2010 timeframe.

I know there’s a lot of controversy and opinions about Backbone, Ember, Angular, etc. I have my own opinions about the quirks and strengths of these kinds of frameworks. But consider that all of them do one basic thing: They abstract a library of code away from the initialization of Javascript code in a way that means that you know that the only things attached to my Backbone view controller object are the things I define in my Backbone “events” parameter for that viewcontroller. Backbone has deficiencies, but since all JQuery attachment is cleanly scoped I’ve never had to worry about haphazard DOM-targetting creating a nightmare of bugs and issues for me.

Outside of Rails, JS developers are working in Node, or PHP(!), or lots of other back-end options. They are writing thick-client Angular apps or Ember apps that talk strictly via JSON APIs to the back-end. A lot of these languages are new, and are having growing pains, but they are evolving our understanding of how to think about a distributed client-server application and address a lot of the headaches with structural advancement.

In the big picture, I’d like to see Rails developers embrace more modern ways of writing Javascript. Personally, I don’t think this has much to do with the Rails-core itself. Rails is a back-end technology, and it’s great at being a back-end technology. It is not meeting the needs of today’s UX and Javascript-heavy world, and that’s OK. There’s lots of ways to structure your Rails app and use the Asset Pipeline to deliver the JS app cleanly an efficiently.

SO again, it sounds like your problem of “memory usage and performance due to lots of event handlers” is the result of years of crufty DOM-targetted Javascript development. Yes, it’s a problem. But no, it’s not Rails’ problem. It the fault of the developers who let it get that way, and their shortsightedness when it comes to seeing the bigger picture and failure to embrace cleaner ways of thinking about a thick-client app.

I realize this may all be a little controversial, so take it with a grain of salt. This is just my opinion having worked in this space for several years and had this specific conversation about Javascript with a lot of different devs (who themselves had different opinions and viewpoints). I think this is actually the heart of one of the challenges for the community moving into 2015.

-Jason

That discussion aside, you can still argue that requiring all the files explicitly instead of implicitly is a win. Then you have an opportunity to think if a file should or not go into a specific manifest.

Also, it makes it easier to understand which files are loaded and in which order.

Best,
Luis Zamith

I think on a even more high level scale that we should start working towards an opinionated rails way of handling Javascript frameworks; whether that is either embracing one of the current ones or creating a new one based on what works best with rails.

The javascipt front end framework is still in it’s growing stage, but it does appear that it is the way the web is moving.

At a minimum, would be great if as a community we start creating boiler plate with that will allow people to easily integrate the 2 way binding of model data the way that ember/angular do.

I would be in favor of simply removing require_tree . from the default application.js file.

While I generally stick with the single javascript file for most of a rails project (as described by Jason),

I find that sometimes there is one or two pages that need a very different set of javascripts.

The problem that I have run in to with require_tree ., is that it becomes impossible to not require

a file.

In any case, I think it is better to encourage requiring files explicitly.

-Amiel