Search code examples
ruby-on-railsruby-on-rails-6webpacker

Using Rails 6, where do you put your “page specific” JavaScript code?


My question here is the same as Using Rails 3.1, where do you put your "page specific" JavaScript code?, just for Rails 6 instead of Rails 3.1.

Suppose I have some JavaScript that I want to use for my posts index page. Where do I put that file, and how do I include it?

In the previous question the answers usually utilize the Rails Asset Pipeline. However, with Rails 6, my understanding is that it uses webpacker instead of the Asset Pipeline for JavaScript files.

Note: I don't want the file to always be included. Eg. if I am on the authors index page, I don't want the JavaScript file for the posts index page to be included. Why? Imagine I have $('li').on('click', changeColor); in the JS file for the posts index page. I probably don't want that code to run on the authors index page, but it will if the file is included. You could get around this problem by namespacing, but I think it would be cleaner (and more performant) to just not include unnecessary files.


Solution

  • I'll describe a few options in order of increasing level of difficulty with regards to experience with Webpack and Webpacker.

    1. Forget page-specific JavaScript and put everything in the application.js bundle. This will most definitely be the easiest approach to understand. For a small application, the tradeoff may well be worth it as having an application that's easier to maintain may outweigh added cost of learning how to best to split up your JS code with Webpack with little performance gain.

    2. Use dynamic imports to lazy load your page-specific code. In this scenario, you would still place all your JavaScript within the application.js dependency graph, but not all of this code would be loaded up-front. Webpack recognizes the dynamic import() function when it compiles your JS and will split the imported chunk out into a separate file that can be loaded on-demand in the browser using a JS framework router or a simple trigger.

      For example, you could have code that looks like this:

      if (document.querySelectorAll(".post-index").length) {
        import("./post/index") // webpack will load this JS async
      }
      
    3. Use page-specific "packs" combined with the splitChunks configuration API. In this scenario, instead of using an application.js pack, you would construct one for each page you want to treat differently, e.g, posts.js, admin.js etc. Using the splitChunks plugin means that your bundles can properly share code. I highly recommend treading carefully with this approach until you understand how Webpack works OR be willing to go through the process of learning Webpack in choosing this path. Webpack typically works best on the assumption you use only one entry point per page, otherwise, you may end up duplicate code across bundles unless you know what you're doing.