如何从 Webpacker 切换到 CSS/JS bundling
最近 Rails 7 正式发布,其中一个引人注目的特性是 CSS/JS bundling,用于取代 Rails 6 的 Webpacker。我在之前的文章中介绍过新的方案带来什么变化。
现在 Rails 创建项目,将会增加两个参数:
-j, [--javascript=JAVASCRIPT] # Choose JavaScript approach [options: importmap (default), webpack, esbuild, rollup]
-c, [--css=CSS] # Choose CSS processor [options: tailwind, bootstrap, bulma, postcss, sass...]
例如要使用 esbuild 和 sass 可以用以下命令:
$ rails new myapp -j esbuild -c sass
但大多数人关心的可能是如何从已有项目上切换到新方案,下面进一步说明。
脚本切换
Rails 增加了两个 gem cssbundling-rails
和 jsbundling-rails
,用来支持 bundling 方案。他们依赖 Rails >= 6,所以不需要升级到 Rails 7 也可以使用。在 Gemfile 中加入以下内容:
gem 'cssbundling-rails'
gem 'jsbundling-rails'
执行 bundle install
之后,用以下命令安装需要的前端工具:
$ bin/rails css:install:sass
$ bin/rails javascript:install:esbuild
通过 git diff
可以看到有什么变动,要注意的地方有:
- 现在 CSS 和 JS 的构建源文件分别是
app/assets/stylesheets/application.sass.scss
和app/javascript/application.js
,需要手工迁移内容。 -
app/assets/builds
是存放编译后静态文件的地方。 - 增加了一个
bin/dev
脚本,通过foreman
一起启动 rails server 和 CSS/JS 构建进程,配置文件是 Procfile.dev。 - 检查 layout 里的标签,删除 webpacker 的引用。
如果项目的文件都按照 Rails 6 默认的目录结构摆放,那么用 bin/dev
启动开发进程就能看到 CSS 和 JS 构建进程在工作。
人工切换
如果你的项目比较复杂,脚本安装不能满足需要,那么可以用人工方式处理。实际上 cssbundling-rails
和 jsbundling-rails
的内容只是一些安装脚本和 Rake task,没有运行时代码。理解如何手工切换到 CSS/JS bundling 可以让配置更符合项目的需求。下面介绍如何实现。
添加 builds 目录
假设项目的静态文件存放结构如下:
app/assets/
├── config
│ └── manifest.js
├── images
└── stylesheets
└── application.css
app/javascript/
└── packs
└── application.js
首先创建 builds
目录,用来存放编译后的文件:
$ mkdir app/assets/builds
$ touch app/assets/builds/.keep
在 .gitignore
中添加这个目录,避免 check in 里面的文件:
/app/assets/builds/*
!/app/assets/builds/.keep
修改 app/assets/config/manifest.js
,删除以下配置:
//= link_directory ../stylesheets .css
添加以下配置:
//= link_tree ../builds
现在 Assets pipeline 就知道要从 builds
目录获取静态文件。
配置 sass
安装 sass
:
$ yarn add sass
在 package.json
中添加以下内容:
"scripts": {
"build:css": "sass app/assets/stylesheets/application.scss app/assets/builds/application.css --no-source-map --load-path=node_modules"
}
创建文件 /app/assets/stylesheets/application.scss
,将原先 app/assets/stylesheets/application.css
的内容迁移进去。
要注意 Assets Pipeline 提供的 require_tree
require_self
等注释不再起作用,要用 sass
的 @import
替代。
如果正常,使用 yarn build:css
可以看到 CSS 编译成功。
配置 esbuild
安装 esbuild
:
$ yarn add esbuild
在 package.json
中添加以下内容:
"scripts": {
"build:js": "esbuild app/javascript/application.js --bundle --outdir=app/assets/builds"
}
创建文件 app/javascript/application.js
,将原先 app/javascript/packs/application.js
的内容迁移进去。
要注意如果之前用的 webpacker,转到 esbuild 有可能需要修改 require 语法,例如 import "channels"
要改成 import "./channels"
。另外依赖 webpack 的语句也需要修改,例如 Stimulus 的 Webpack Helpers 将不可用。
如果正常,使用 yarn build:js
可以看到 JS 编译成功。
添加 bin/dev 脚本
现在开发环境需要启动三个进程,一个 rails server,一个 sass,还有一个 esbuild。每次开发启动这三个进程会比较繁琐,所以可以借助一些工具管理,这里以 foreman 为例。
添加一个 bin/dev
文件,内容为:
#!/usr/bin/env bash
if ! command -v foreman &> /dev/null
then
echo "Installing foreman..."
gem install foreman
fi
foreman start -f Procfile.dev
给 bin/dev
添加可执行权限:
$ chmod +x bin/dev
添加文件 Procfile.dev
,内容为:
web: bin/rails server -p 3000
css: yarn build:css --watch
js: yarn build:js --watch
这样开发的时候就可以用 bin/dev
一起启动三个进程。
添加 Rake task
最后是让 Assets Pipeline 编译静态文件的时候正确的调用外部编译。
添加文件 lib/tasks/build.rake
,内容为:
namespace :build do
desc "Run yarn install"
task :install do
system "yarn install"
end
desc "Build Javascript and CSS"
task :all => [:javascript, :css]
desc "Build JavaScript"
task :javascript => :install do
system "yarn build:js"
end
desc "Build CSS"
task :css => :install do
system "yarn build:css"
end
end
Rake::Task["assets:precompile"].enhance(["build:all"])
如果正常,现在执行 bin/rails assets:precompile
时会自动执行 build:css
和 build:js
。
以上就是手工操作切换 CSS/JS bundling 的过程。根据你项目的复杂程度可能需要进行相应的更改。在切换完成后,就可以删掉 webpacker 相关的包和配置。
总结
看完如何手工切换 bundling,你应该会发现 bundling 的操作是在 Assets Pipeline 之前完成的。编译完成后把文件放到 builds
目录,然后告诉 Assets Pipeline 从这个目录读取文件就行了。
这是一次把 Rails 主体和前端工具链解藕的过程,我们可以用任意偏好的工具和方式处理前端文件,只要最后把它纳入 Assets Pipeline 管理就行了。这大大增加了灵活性,也不需要和框架的默认配置做斗争——这都取决于自己的选择。
希望这篇文章有助于理解如何按 Rails 7 的方式管理静态文件。