RailsConf 2019

Webpacker vs Asset Pipeline

Webpacker vs Asset Pipeline

by Danielle Gordon

In her talk at RailsConf 2019, Danielle Gordon explores the differences and considerations of using Webpacker versus the traditional Asset Pipeline in Rails applications. She emphasizes that the discussion is not about determining a clear 'winner' between the two, but rather understanding the strengths and weaknesses of both approaches to help developers make informed decisions based on their specific project requirements.

Key points discussed include:

- Overview of Asset Pipeline: The asset pipeline is part of Rails that manages the concatenation, minification, and compression of JavaScript and CSS assets using the Sprockets gem. It simplifies the organization of assets through import statements.
- Extensibility of Sprockets: Developers can extend Sprockets with transformers, processors, and compressors to customize asset handling. Transformers modify file types, while processors manage how files are loaded and optimized.
- Introduction to Webpacker: Webpacker acts as a wrapper around Webpack, designed specifically for modern JavaScript applications. It offers an efficient way to manage JavaScript dependencies and allows for various types of files to be imported seamlessly.
- Dependency Management: Webpack builds a dependency graph that simplifies the organization of assets, ensuring that files are only loaded once, unlike Sprockets, which concatenates all files.
- File Organization: Gordon compares the organization of files within Rails applications when using Asset Pipeline and Webpacker, highlighting the placement of JavaScript, CSS, and images within the app directory structure.
- Developing Components: The talk illustrates how components can be created and bundled with both methods, noting that while Webpacker streamlines the process, Sprockets may require additional steps, especially with the need for dual import statements.
- Case Study at Nipt: Gordon shares her experience at Nipt, where the team chose a mix of both methods depending on the dynamic nature of their pages. They found success with using Rails view partials due to the static nature of many of their pages.
- Conclusions and Recommendations: Ultimately, Gordon suggests there isn’t a right or wrong choice. The decision to use Webpacker or Asset Pipeline should be guided by the project needs, with Webpacker being preferable for more JavaScript-heavy or single-page applications.

The presentation concludes with a reminder that both tools have their unique advantages, and understanding these allows developers to choose the best approach for their Rails applications.

00:00:20.750 Okay, so this is not going to be a very long talk. Hopefully, it'll be useful. I'm Danielle, and I'm a software engineer at a company called Nipt based in Boston.
00:00:24.090 I'm actually going to start this talk by changing the title. Originally, I decided to call it "Webpacker versus the Asset Pipeline," but as I continued to do research for this talk, I realized that it's not really just Webpacker versus the Asset Pipeline. It's more like Webpacker versus no Webpacker or Webpacker versus the Asset Pipeline plus some other real stuff.
00:00:34.650 Honestly, what this talk is really about is discussing what it's like to use a Rails app with Webpacker and without it. It's honestly not a straightforward comparison because I don't think there's going to be a clear winner at the end. It's more about personal preference, and I really want you to see what to expect when you choose one over the other.
00:00:55.350 Webpacker is a wrapper around Webpack, which is something you use with JavaScript applications. I decided to use UJS, so we’re going to see a lot of examples that use .vue files. A typical .vue file will look like this. I know it may be really small, and you don’t necessarily need to read it; I just want you to understand that a .vue file can consist of three different parts.
00:01:18.140 You’re going to have your template part, which is just HTML. You’re going to have your JavaScript part, and you’ll have your CSS part. And of course, the order of this does not matter; this is just the order I happened to put it in.
00:01:41.459 Now, what is the Asset Pipeline? When you go to the Ruby guide, it states that the Asset Pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. We’ve all started a new Rails app, right? And we’ve seen something similar to this in our application.js file. This is where we are just importing either sub-trees or folders or importing another file and concatenating it into this current file, all of which is handled by Sprockets, the gem that implements the Asset Pipeline.
00:02:06.569 Similarly, in our Sass files, we’ve seen these import statements where we concatenate other CSS files to our current Sass file. We’ve also seen fingerprinting, where our assets have fingerprinting added so that it can make caching easier. It’s important to note that while Sprockets can handle this, you may not know that Sprockets can be extended.
00:02:28.909 We can extend it by adding processors, transformers, or compressors. There are many resources I used to research some of this, and I urge you to look at that URL down there; it will provide you with more information. For now, let's focus on the three types of extensions we can use.
00:03:04.150 There's also one more type of extension called exporters, which just writes the files to the file system, but we won't discuss that much. We’ll start with transformers. Transformers change one file type into another. For example, a CoffeeScript file, a .coffee file, will be transformed into a JavaScript file. If you ever get the chance to look at different source codes, such as the TypeScript source code, you can see where it's registering MIME types.
00:03:21.340 For instance, it registers text/typescript with the .ts extension and a transformer that changes the TypeScript file to a JavaScript file using the TypeScript Rails transformer. Essentially, that's what a transformer does: it changes one file into different file types. In the case of Sass files, their default transformer converts them into CSS files.
00:03:39.040 After transformers, we have processors. There are two types of processors: pre-processors and post-processors. Pre-processors run when the file is loaded, while post-processors run after transformers. If you recall the lines I mentioned in application.js, that is handled by a preprocessor called the directive processor. It is responsible for loading files and concatenating them.
00:04:15.510 An example of a post-processor is Autoprefixer, which takes CSS like this and expands it into more compatible code for various browsers. So, we’ve reviewed three types of processors, and then we have compressors. Compressors do exactly what their name implies; they reduce the size of your files. For example, UglifyJS is a process that acts as a compressor by removing whitespace to minimize the file size.
00:04:55.110 We’ve seen how we can extend Sprockets by adding processors, transformers, and compressors. Now, let's look at Webpack. As mentioned earlier, Webpacker is a Rails wrapper around Webpack. If you visit the Webpack website, they describe Webpack as a static module bundler for modern JavaScript applications.
00:05:02.310 However, this definition doesn’t quite illustrate what it does. What helped me understand Webpack better was the diagram on their homepage. You can see that you have JS files dependent on Vue files, which in turn depend on other Vue files, and Sass files that depend on image files. Webpacker takes all of these and compiles them into simple static assets. That’s essentially what Webpacker does; it manages all of your dependencies for you.
00:05:53.610 All you need to do is specify that your file depends on these things, along with which other files depend on those, and Webpacker will manage all those dependencies and create simple static files from them.
00:06:18.270 In using Webpack, you'll encounter import statements like these. One of the cool things about Webpack is that you can import almost any type of file into your JavaScript files. You can import classes, functions, and objects from other JavaScript files and even images and CSS into your JavaScript files.
00:06:35.830 When configuring Webpack, you want to start with an entry point. Your entry point is the file or files that you will compile down. These are typically located in your app’s JavaScript pack folder. Any file within that folder is an entry point, including .vue files, .js files, Sass files, CSS files, and even image files. From here, we pass them through loaders.
00:07:21.180 Loaders, like Sprockets’ transformers, processors, and compressors, encompass similar functionalities. Here’s an example of a loader that works with a Sass file. The great thing about loaders is that you can specify a regex for them. For instance, you can indicate that you want any file ending with .sass to be processed. Note that loaders run in reverse order; unlike the intuitive top-down processing, they work bottom-up.
00:07:46.160 For instance, the Sass loader compiles the .sass file into a .css file, then that gets passed to a CSS loader to interpret the import statements, followed by a style loader that adds the CSS to the DOM. In this way, Sprockets’ Sass loader acts as a Sprocket transformer, and if you wanted to, you could put an Autoprefixer at the top to run last.
00:08:24.510 Right now, we are using a Vue loader, which will convert that Vue file, with the .vue extension, into corresponding JavaScript and CSS files. Therefore, with loaders such as Vue loader, Sass loader, and CSS loader, we finally arrive at our outputs.
00:09:00.600 By default, output will direct to your public packs folder, where all your minimized and compressed files will reside. For your .vue and .js files, you'll have .js files, and for your Sass files, you'll have .css files. Additionally, you'll also obtain source map files, which are incredibly useful for debugging JavaScript in production.
00:09:39.640 These source map files allow you to map the minified JavaScript back to the original code, giving you insight into what's occurring. We now understand how Sprockets works, how we can extend it, and how Webpack works. Now we can ask more questions, such as where assets are located, how we create components, how we bundle files, and how we manage CSS.
00:10:02.330 By default, all your files for entry points will be placed in app/javascript. The entire application.js file serves as the area to place all your entries while using Webpack. Even image files that you import into a JavaScript file would still reside in your app/javascript folder.
00:10:46.130 This may feel a bit odd compared to the Asset Pipeline, where everything is in app/assets. Similarly, compiled and fingerprinted files with Webpack will end up in public/packs, whereas with the Asset Pipeline, they would be in public/assets.
00:11:20.280 Now that we know where our files are and where they will end up, how do we create components? Recalling the Vue file we mentioned earlier, that Vue file conveniently contains our template, JavaScript, and CSS. Webpack manages this for you seamlessly. However, Sprockets can't handle this due to its limitation with .vue files.
00:11:58.410 You can extend it with a transformer. If you happen to find that Vue transformer, it enables transforming a .vue file to a JavaScript file or a CSS file. And of course, for every Ruby programmer, there’s probably a gem for it. Indeed, there is a Rails view loader gem available that allows you to utilize this compilation.
00:12:27.820 But what happens if a Sprocket transformer isn't available, or you prefer not to create one due to time constraints? In that case, you can utilize Rails view partials. While some may feel uneasy about mixing JavaScript with HTML, it is achievable with script tags.
00:12:55.460 There is usually a performance hit when continually importing Rails view partials, but you can use caching to alleviate this. Caching is manageable since it doesn’t depend on user data; it's purely static HTML and JavaScript.
00:13:24.040 Now we know how to create our components, but how do we bundle everything properly? At the top level, including your JavaScript and CSS links is quite similar to doing it with Sprockets. Instead of using a JavaScript include tag, you would use a JavaScript pack tag. Likewise, while using a stylesheet link tag, you'll use a stylesheet pack tag.
00:13:52.050 Suppose we are utilizing a Vue Sprocket transformer. In that case, it’s essential to note that even though that Vue Sprocket transformer is in play, you still need to add the Vue file to any JavaScript file carrying that JavaScript logic. Additionally, ensure to add the component's Vue file to your CSS file, necessitating an import statement in both locations.
00:14:26.460 If you only add it in one place, say, to your JavaScript file, but neglect to do the same in your CSS or Sass file, you will miss out on getting any of the relevant styles. Webpack, however, does not require this dual inclusion; it simplifies things significantly by allowing you to import any type of object swiftly into a JavaScript object.
00:15:02.460 As a brief overview of import and export: if you do not export something from a JavaScript file, you cannot use it in another file. To export an entire file, you can enclose it within export brackets. Moreover, you can export multiple items using named exports.
00:15:37.370 For instance, if you have a main JavaScript file that imports one or more files, Webpack builds a dependency graph of everything in your static files. Therefore, it includes each code snippet from imported files just once, greatly optimizing the compilation process. In contrast, Sprockets concatenates the imports, which can lead to duplicate entries if not handled carefully.
00:16:02.400 This presents an advantage for Webpack, as it ensures you include unique dependencies, while Sprockets risks unnecessary repetition.
00:16:34.820 With that, we have explored various aspects of file bundling. Now, how do we handle CSS? CSS management within Webpack is relatively straightforward because you can add CSS directly to the entry points. Should you wish to include global CSS, you can incorporate it into any JavaScript file as needed.
00:17:07.450 In summary, adding CSS using Webpack aligns with traditional methods and allows for Sass import statements even in .vue files, ensuring seamless integration with existing methodologies.
00:17:36.690 At Nipt, we've opted for the Rails view partial method due to the static nature of many of our pages, which suit our needs well. While opting for a full single-page application (SPA) may appeal to some, using Webpack can certainly be beneficial for handling dependencies, particularly with JavaScript applications. However, I believe there is no right or wrong approach to this.
00:17:57.750 Ultimately, I hope this talk has been useful to you, and I welcome any questions you may have. Thank you.