What is Webpacker?
The goal of Webpack, or any front-end build system,
is to allow you to write your front-end code in a way that is convenient for
developers and then package that code in a way that is convenient for browsers.
As of Rails 6, Rails started to bundle and wrap Webpack inside the Rails
applications. This is done through
Webpacker.
Webpacker provides a pre-configured Webpack along with view helpers to easily
get corresponding generated assets like JavaScript, CSS and SCSS files.
Webpacker related files
The Webpacker installation process calls the yarn
package manager, then
creates a package.json
file with a basic set of packages listed, and uses Yarn
to install these dependencies.
Along with that the following local files are created by Webpacker:
File | Location | Explanation |
---|
JavaScript Folder | app/javascript | A place for your front-end source |
Webpacker Configuration | config/webpacker.yml | Configure the Webpacker gem |
Babel Configuration | babel.config.js | Configuration for the Babel JavaScript Compiler |
PostCSS Configuration | postcss.config.js | Configuration for the PostCSS CSS Post-Processor |
Browserlist | .browserslistrc | Browserlist manages target browsers configuration |
JavaScript codebase in Rails
There is a app/javascript
directory to host all the front-end related code, or
more accurately all the JavaScript and related code.
This is how that directory would look like:
The channels
directory is generated by Action Cable component of Rails. It's
not that significant for us at the moment.
The stylesheets
and src
directories are usually created by us, manually.
The packs directory
Typically, the file in the packs
directory will be a manifest that mostly
loads other files, but it can also have arbitrary JavaScript code.
It is important to note that only the Webpack entry files should be placed in
the app/javascript/packs
directory.
Webpack will create a separate dependency graph for each entry point, so a large
number of packs will increase compilation overhead.
If you want to change the default packs
directory path, you can adjust the
source_path
(default app/javascript
) and source_entry_path
(default
packs
) in the config/webpacker.yml
file.
Within source files, import
statements are resolved relative to the file doing
the import
, so import Bar from "./foo"
finds a foo.js
file in the same
directory as the current file, while import Bar from "../src/foo"
finds a file
in a sibling directory named src
.
Difference between Webpacker and Sprockets
Rails also ships with Sprockets, an asset-packaging tool whose features overlap
with Webpacker. Both tools will compile your JavaScript into browser-friendly
files and also minify and fingerprint them in production.
In particular, code can be added to Sprockets via a Ruby gem.
You should choose Webpacker over Sprockets on a new project if you want to use
NPM packages and/or want access to the most current JavaScript features and
tools.
For transitioning from Sprockets to Webpacker, we can make use of the following
tools to achieve tasks:
Task | Sprockets | Webpacker |
---|
Attach JavaScript | javascript_include_tag | javascript_pack_tag |
Attach CSS | stylesheet_link_tag | stylesheet_pack_tag |
Link to an image | image_url | image_pack_tag |
Link to an asset | asset_url | asset_pack_tag |
Require a script | //= require | import or require |
Using CSS with Webpacker
Out of the box, Webpacker supports CSS and SCSS using the PostCSS processor.
To include CSS code in your packs, first include your CSS files in your
top-level pack file as though it was a JavaScript file.
So if your CSS top-level manifest is in
app/javascript/stylesheets/styles.scss
, you can import it with
import stylesheets/styles
.
This tells Webpack to include your CSS file in the download.
To actually load it in the page, include
<%= stylesheet_pack_tag "application" %>
in the view, where the application
is the same pack name that you were using.
Significance of stylesheet pack tag
We had replaced the stylesheet_link_tag
with stylesheet_pack_tag
in the
setting up Shakapacker chapter.
The reason is that in production
environment, webpacker by default sets
extract_css
to true
.
The way we tell Webpack which files to load is by using import
or require
statements. This includes CSS files, images, and everything else.
When we do import '../stylesheets/application.scss'
, we're telling Webpack to
include application.scss
in the build.
This does not mean it's going to be compiled into our JavaScript, but only that
Webpack now knows that you want to compile this file.
How that file compilation is handled will depend upon how our loaders
(css-loader, sass-loader, file-loader, etc) are configured.
When we do <%= stylesheet_pack_tag 'application' %>
, that's a run-time
inclusion from Rails, that has nothing to do with whether Webpack has built the
stylesheets or not.
All that line is doing is saying "if you have a pack named application-*.css
,
include it here".
If Webpack doesn't build a separate pack of CSS, then this statement won't have
anything to load, because no stylesheets were compiled.
Handling Webpacker config for each environment
Webpacker has three environments by default development
, test
, and
production
.
You can add additional environment configurations in the webpacker.yml
file
and set different defaults for each environment.
Webpacker will also load the file config/webpack/<environment>.js
for
additional environment setup.
Webpacker during deployment
Webpacker adds a webpacker:compile
task to the assets:precompile
rake task,
so that any existing deploy pipeline that was using assets:precompile
would
work.
The compile task will compile the packs and place them in public/packs
.
There might be times when we have to debug issues with asset compilation as it's
done in production.
We can run the following command to simulate such a compilation locally:
Downside of not running Webpack development server during development
When you don't have a Webpack development server running, then while trying to
load the pack assets using Webpacker helper methods like javascript_pack_tag
or image_tag
, Rails will compile your entire assets on demand rather than
upfront.
This can be noticed when conditionally rendering CSS with respect to the React
component's state using the classnames
library. In such a case, Webpack will
boot from scratch and re-compile. It can make reloading the page extremely slow.
For this reason it is recommended to use a development server during
development.
Advantages of running Webpack development server during development
During the development, you will find yourself making changes to your JavaScript
and CSS files very frequently.
Thus reloading after each change will be really slow and time consuming if you
are not running Webpack development server. The reason is that by default
Webpacker, tries to compile assets from scratch with each request.
One of the biggest upside of running the development server alongside the Rails
server is that it will detect any changes to your JavaScript and CSS and
re-compile it automatically, in real time without much delay.
Reloading manually before the development server has finished re-compiling, will
only reload the same old assets. After compilation is done the browser window
running your application will automatically try to fetch the latest assets.
Another benefit of running a Webpack development server along with the Rails
server is that it makes catching errors specific to the backend and frontend
easier.
When we don't run a Webpack development server, any errors regarding compilation
of static assets are logged to the standard Rails log. This is really hard to
debug since it's only a single terminal window showing all the errors.
Whereas, upon running a Webpack development server, all compilation errors or
changes in static assets are reflected in only the terminal running the
webpacker-dev-server
.
This will make debugging easier since the errors specific to the frontend side
will be logged in the terminal running Webpack development server and the errors
specific to Rails side will be logged in the terminal running Rails server.