Talks

Webpacker, It-Just-Works, But How?

Webpacker, It-Just-Works, But How?

by Justin Gordon

In the talk 'Webpacker, It-Just-Works, But How?' presented by Justin Gordon at RailsConf 2020, the speaker explores the intricacies of integrating the Webpacker gem with Ruby on Rails to facilitate seamless Webpack integration. Gordon, an experienced developer in both Rails and Webpack since 2014, discusses the evolution of Webpack and its accompanying complexities, establishing that despite Webpack's rich functionality, effective use within Rails requires minimal user intervention. He aims to demystify how Webpacker orchestrates tools and configurations to deliver a smooth development experience for both the developers and end-users.

Key points covered in the talk include:

  • Objectives of Webpacker: Ensuring performance for end-users and ease of use for developers, emphasizing the importance of minimal configuration and an optimal experience across environments.
  • Key Functionalities: Introduction to the main components of Webpacker, including view helpers, asset fingerprinting, bundle splitting, and the use of Webpack dev server for an improved development cycle.
  • Understanding the Flow: A detailed explanation of how Rails Webpacker orchestrates Webpack to produce manifest JSON along with static assets, including JavaScript and CSS files, which are essential for the application's functioning.
  • Development Environments: Discussing the challenges in managing different Rails environments (development, production) and how Webpacker accommodates these through its setup.
  • Effective Configuration: Gordon illustrates how environmental configurations (development.js, production.js) are crucial for customizing the build process without overcomplicating the setup.
  • Hot Module Replacement & Optimization: The use of Webpack features such as hot reloading and code splitting to improve user experience and significantly reduce load times for web applications.
  • Deployment Considerations: The process of deploying assets using Rake tasks and how Webpacker enhances the asset pre-compilation step, ensuring that required resources are available in production.

Gordon punctuates his discussion with real-world examples from his consulting experiences, underscoreing the importance of understanding Webpack's configuration as a foundation for managing JavaScript assets effectively. Additionally, he offers practical advice on version management and custom configurations that can help developers avoid common pitfalls associated with updates and dependencies.

The main takeaway from the session is that by harnessing the capabilities of Webpacker, developers can significantly streamline their Rails applications’ integration processes with Webpack, while minimizing the configuration burden. By understanding the underlying mechanics, developers can better control their development and production setups, ensuring optimal performance and maintainability.

00:00:09.040 Hi, my name is Justin Gordon. Welcome to my talk, 'Webpacker: It Just Works, But How?'
00:00:16.640 So why should you listen to me? Well, I know a bit about webpack, React, Ruby on Rails, and a bit about Webpacker.
00:00:22.480 I've been working on this topic since 2014 when I wrote an article about integrating webpack with Ruby on Rails.
00:00:29.519 Then, in 2015, I created the library 'React on Rails,' which is probably one of the most popular ways to integrate Ruby on Rails with React, specifically for server-side rendering.
00:00:37.120 This library has garnered about 4.2 million downloads and is being used by approximately 540,000 websites.
00:00:42.960 Since then, I've been running my own consulting company, ShakaCode, helping companies optimize their Rails websites.
00:00:48.960 My team at ShakaCode is also building Rails apps with modern front-ends, like our startup app, HiChee.
00:01:00.640 So why did I create this talk? I was working on a project for my client, Popmenu, which has restaurant websites.
00:01:08.159 This is a React on Rails site, and we were setting up loadable components for code splitting, trying to make it work with the React Refresh webpack plugin.
00:01:15.840 With that setup, we could achieve hot reloading, but it turned out to be quite challenging.
00:01:21.680 This led me to delve deeply into Rails Webpacker, and I realized I had a lot to share with you.
00:01:29.280 So, what's the problem we're solving, and what is the goal? I read a great quote today: 'When you understand the problem, what to do becomes obvious.' Understanding comes from multiple perspectives.
00:01:42.640 What we're trying to achieve is to put Rails and webpack together, aiming to make it just work. But who does it need to work for? First, it must work for the end users of web applications because slow websites lead to frustrated users who abandon them.
00:02:08.879 It also has to work well for developers; we want it to be easy to write and maintain the code.
00:02:15.040 Finally, it has to work for the Rails Webpacker maintainers, as this assists not only contributors but also serves as documentation for library users.
00:02:20.160 So, what are the main elements that make it just work? First of all, minimal to zero configuration is needed for webpack, making it easy for Rails developers.
00:02:51.200 We want Rails view helpers to support fingerprinting and bundle splitting. We also have the webpack dev server, which we want to make easy to run.
00:03:00.160 We want to ensure webpack compilation occurs when necessary and simplify project setup for production deployment. Rails has long had asset preparation with Sprockets, but Rails 6 has made Webpacker the default JavaScript bundler through the new Application JavaScript directory.
00:03:41.360 The two main components of Rails Webpacker will be orchestrating webpack to create the `manifest.json` and also handling the static assets like JavaScript and CSS files.
00:04:09.920 Additionally, we need to provide view helpers so that Rails views can display the script and link tags, indicating to the browser which files to download.
00:04:24.960 This may sound straightforward, but there are several key challenges. Firstly, the webpack output and Rails view helper output will vary based on the Rails environment.
00:04:43.200 The development environment can optimize for developer experience while the production environment should focus on the end-user experience.
00:05:04.200 Another challenge is the webpack dev server for development, differing from the static asset creation that happens in the public directory.
00:05:11.360 During development, assets may either be served from the public directory or from the webpack dev server; I'll elaborate on that shortly.
00:05:34.800 And lastly, there's the challenge of creating a separate bundle for server rendering, which involves creating distinct files.
00:05:42.960 So, what does the overall Rails Webpacker flow look like? I'll refer back to this diagram frequently throughout the talk. The key point is orchestrating webpack.
00:06:14.880 We'll set some settings in a `config/webpacker.yml` file. This was done because both Rails and the JavaScript code need to access that configuration.
00:06:37.360 In conjunction with the JavaScript files that will configure webpack, they reside within the Rails Webpacker libraries alongside other Ruby code.
00:06:49.120 These will create a webpack config object which will then run webpack, generating the `manifest.json` and the static assets that Rails needs. This will construct the HTML that gets sent to Rails, creating the correct asset tags, whilst ensuring Rails is ready to serve the statically created assets.
00:06:54.640 Let me show you a demonstration of the end result. I've built a sample application on GitHub: ShakaCode/webpacker-examples.
00:07:14.960 I'll start up Rails. I've cleared out my webpack directory in public, so the files are absent, making it necessary for webpack to compile.
00:07:54.240 Let's hit refresh. Now, notice that Webpacker is compiling.
00:08:00.960 Everything appears to be up to date, but if I check this in the network tab, I can view what files were served.
00:08:41.280 If I then view the page source, this shows what we wanted to achieve within the HTML generated by Rails.
00:08:52.960 We see the link to the files created by webpack—this is the magic of Rails Webpacker orchestrating webpack and ensuring the necessary files are available on our web page.
00:09:03.520 Now, how do the new view helpers function? Currently, we're focused on how Rails delivers the HTML with proper asset tags and how Rails serves the assets for the web page.
00:09:12.960 Let’s dive deeper. In our view, specifically the `index.html.erb`, we'll use the Rails Webpacker helper method `stylesheet_pack_tag`, which references our 'home' string.
00:09:36.160 This 'home' corresponds to our file situated in the packs directory, forming our entry point. Consequently, the public development `manifest.json` produced by webpack will contain an entry labeled 'home'.
00:09:58.560 The `config/webpacker.yml` includes a public output path for development, where the files generated by webpack are stored, directly impactful for Rails’ public assets.
00:10:25.840 This connection between the `manifest.json`, public output path, and view helpers results in the HTML delivered to the browser. As I just mentioned, the HTML the browser renders shows the webpack development runtime for home that directly corresponds to this `manifest.json`.
00:11:08.320 Additionally, if you review those chunk files, you'll see examples in the project I provided. One page contains moment.js, another page contains both lodash and moment.js, and yet another page does not include either.
00:11:46.240 This results in several distinct chunks. Therefore, depending on the page being accessed, we send only the necessary JavaScript to the browser.
00:12:04.400 It would be inefficient to send all JavaScript files as someone might never access certain pages. This showcases a key difference when utilizing Sprockets, which lacks this automated chunking feature.
00:12:15.440 How does webpack know to enact this proper functionality? Let’s explore the overarching roadmap for Rails Webpacker and how the webpack library orchestrates webpack to produce the right output fed into Rails.
00:12:50.880 First, we establish our configuration; it involves multiple JavaScript files that correspond to different environments. The only environment-specific files are `development.js`, `production.js`, and `test.js`.
00:13:16.240 The `environment.js` file serves as the common file imported by both development and production configurations. Remember, the `config/webpacker.yml` file contains essential standard directories and other configurations that are vital on the JavaScript side.
00:14:31.320 On the client-side, there are also entry points; the entry point example of 'home' is straightforward.
00:14:37.680 It does not include additional complexities found in the examples utilizing lodash and moment. A crucial file here is the `package.json`, which grants access to numerous modules.
00:14:49.840 So what happens is that all these files, JavaScript files, images—everything—are funneled into webpack, ultimately generating multiple files in your public webpack directory.
00:15:13.440 Most importantly, it generates this manifest file that allows Rails to map specific files to bundles.
00:15:53.120 How does webpack actually run? By installing Rails Webpacker, a bin file called `bin/webpack` is created.
00:16:11.040 This file calls the webpack runner with the necessary arguments and loads the appropriate webpack configuration based on your node environment.
00:16:23.440 Remember, this is not your Rails environment; you can configure your node environment to determine the output generated by webpack and your JavaScript files.
00:16:49.040 For example, the `development.js` file will correlate with the development environment configuration, as mentioned earlier.
00:17:05.200 This overall orchestration flow is responsible for preparing those files for Rails, specifically the `production.js` file that contains production-specific configurations.
00:17:37.680 It references the common `environment.js` file I previously mentioned, ensuring a shared foundation.
00:18:01.920 The key takeaway is that the output—this file in line five ultimately exports a straightforward JavaScript object representing the webpack configuration.
00:18:24.400 That webpack configuration can be examined in precise detail over at the webpack site, specifically webpack.js.org/configuration.
00:19:00.560 In the `environment.js` file, this is where you place customizations applicable to all environments.
00:19:28.080 Now let's delve under the hood and take a closer look at what happens under the surface.
00:19:56.960 Suppose we want to inspect the webpack configuration for debugging.
00:20:22.480 To do that, I will exit the Rails app and run `bin/webpack --debug`.
00:20:44.560 By running this, the debugger opens, and I've made some adjustments, setting the default setup to export environment to the webpack config while allowing me to inspect it.
00:21:20.080 You can inspect it directly in the console for more space. The webpack configuration provides details about file names, including public paths.
00:21:35.760 Importantly, you can see the environment plugins that could include different other settings. This configuration is ultimately a nested JavaScript object, not merely a plain JSON file.
00:21:52.080 Using the inspector helps clarify the structure, and you may visit webpackjs.org to reference documentation.
00:22:15.920 The entry points, configured by the packs directory, tie the documentation to the actual object defined there.
00:22:59.440 As I referred to earlier, that object is produced through executing Rails Webpacker, unifying these files to generate this config object destined for webpack.
00:23:14.240 Another point to note is regarding your `package.json`; sometimes it appears lacking in webpack or Babel plugins and their specific versions.
00:24:06.320 When including dependencies from node modules, it's akin to Ruby gems, where various dependencies can reside.
00:24:30.080 To accurately discern what dependencies are being pulled in, especially for Rails Webpacker, maintain awareness of how dependencies specify plugins, including the webpack version.
00:25:02.440 A recommended first step is to check the official GitHub repository for the Rails Webpacker project and ensure browsing the correct version number corresponding to your webpack version.
00:25:36.720 Currently, as of this recording, Webpacker is at version 5.1.1. If running on version 4 and checking out the master file, discrepancies in documentation versions could cause issues.
00:26:11.440 Another option to assess this is running `npm ls webpack` or similar commands to identify included versions.
00:26:44.080 Take caution; the webpack documentation lacks easy access to older versions.
00:27:01.920 While there are not frequent changes from version to version, keeping them in mind is wise.
00:27:30.480 If you wish to override included versions being pulled from webpack, yarn resolutions allow you to specify desired versions directly.
00:27:51.760 To maintain coherence with the standard Webpacker configurations, it's advisable to keep deviations minimal, focusing primarily on JavaScript configurations over the `webpacker.yml` setup.
00:28:30.720 Creating separate bundles for client and server rendering using Rails Webpacker is achieved via the webpack multi-compiler.
00:29:05.400 This process allows webpack to export an array of configurations instead of being limited to a single object.
00:29:39.120 For instance, merging the client environment with a basic webpack config will create a client specific configuration, while keeping a server configuration independent.
00:30:17.760 It's not uncommon to require different setups for client and server bundling, especially in the context of server-side rendering.
00:30:56.640 Another common inquiry revolves around whether view helpers can be utilized separately from Rails Webpacker configurations, and the answer is yes.
00:31:33.920 Many of my React on Rails clients require customized webpack configurations, leading me to devise approaches for leveraging the Rails Webpacker library while keeping the complete webpack config separate.
00:32:10.880 It’s entirely feasible to draw upon the Rails Webpacker Node package. The pivotal part is ensuring the right `manifest.json` is generated.
00:32:54.560 Moreover, if seeking to incorporate hot reloading, paying close attention to utilizing that node package is critical.
00:33:32.160 What’s the ideal method for running Rails during development? Earlier, I demonstrated how running `rails s` works, turning compilation on.
00:34:12.720 Facilitating the web request means that if some JavaScript changes synchronize from GitHub, having the compile option set to true will cause webpack compilation to be skipped.
00:34:50.480 If the webpack dev server is inactive, Rails computes a cache key for your assets, consuming time by reading each client file.
00:35:09.840 This procedure can be noticeably slow if numerous files are present. The compilation process I demonstrated pops up retaining cache keys from past operations.
00:35:36.480 At the end of the day, the serving time on Rails can prolong significantly if the compile option is engaged without taking into account the subsequent options.
00:36:16.400 Option two is to run webpack in watch mode by launching `rails s` in one terminal and `bin/webpack watch` in another.
00:36:41.920 This ensures webpack actively regenerates the bundles anytime a file is saved. Option three involves running Rails in one terminal while the webpack dev server operates in another.
00:37:07.440 When the webpack dev server runs, it computes the bundles proactively, similar to watch mode, but serves as a mini web server for your assets.
00:37:39.920 However, when examining the source of your Rails output while the dev server is active, nothing visibly differs—this aspect showcases the 'magic' of Rails Webpacker.
00:38:12.160 Notably, Rails skips the webpack compilation if the compile option is set to false, with Rails Webpacker pragmatically checking whether the dev server runs on localhost at port 3035.
00:38:55.520 To maintain seamless integration, Rails Webpacker ensures asset management remains stable, utilizing a proxy to the webpack dev server.
00:39:12.960 This proxy setup occurs within the Rails Webpacker's Railtie initializer, facilitating smooth configurations through the YAML settings.
00:39:41.920 In conclusion, if your development JS file is sparse, remember that the vital settings derived from the environment package relate back to the Rails Webpacker Node library.
00:40:16.640 These configurations are not merely default; they are tailored to per-environment specifics, whether it be development or production.
00:40:40.160 The final aspect of Rails Webpacker magic centers around asset deployment. You might have used `rake assets:precompile` for deployment previously.
00:41:07.520 However, Rails Webpacker automates this process for you. There's a `compile.rake` file within the Rails Webpacker source code.
00:41:32.560 This file contains the method `def enhance_assets_precompile`, integrating dependency management to ensure Yarn installation.
00:41:56.880 It subsequently calls the Webpacker compile task, orchestrating webpack to construct all assets.
00:42:17.920 Congratulations, you've almost reached the end of my Rails Webpacker presentation. To recap, the main components of the overall flow begin with the `config/webpacker.yml` file.
00:43:01.280 This file is crucial as it comprises values that the Ruby on Rails server and JavaScript code will read to configure the webpack configuration object.
00:43:28.720 You will also have individual files for each node environment—development, test, and production—allowing for further customization of webpack.
00:44:06.160 This orchestration leads to the creation of our configuration object that gets fed into webpack.
00:44:28.480 This process generates the `manifest.json` and additional static assets such as JavaScript, CSS, fonts, and images necessary for the browser.
00:45:03.920 The Rails server utilizes this asset information from the manifest to execute appropriate asset tags in the pages sent to the browser.
00:45:37.520 Because Rails Webpacker efficiently configures webpack, the actual static assets are stored directly in the public directory, ready for integration.
00:46:15.440 Thank you for joining this talk; I hope you now have a clear understanding of how to harness Rails and webpack using the Rails Webpacker gem and Node modules.
00:47:03.920 My wish is to aid you in effectively configuring your assets to ensure your client-side components are optimized for all environments.
00:47:46.720 I recognize there may be some complications, and this presentation aims to guide you in avoiding unnecessary challenges.
00:48:01.920 On the final slide, I've provided resources should you encounter any issues with Rails Webpacker, server-side rendering, or enhancing performance.
00:48:11.920 Feel free to explore the links to an invite for a Slack workspace, a forum I'll attempt to contribute to, and my email for any direct inquiries.
00:48:32.160 This area has been my niche for a long time, and I'm eager to help you with your Webpackers inquiries. Thank you once more for attending.
00:49:00.000 If you obtain the slides, you'll encounter bonus slides that cover aspects beyond those discussed in the talk.
00:49:39.840 A quick appreciation to JetBrains, ShakaCode's clientele, and other contributors. You can find the details in the slides.
00:50:03.200 Thank you once again for being here, and I hope you experience minimal yak shaving with Rails Webpacker moving forward!
00:50:25.760 Goodbye!