Propshaft 简介

有留意 Rails 更新的人应该会注意到,Rails 7 新增了一个组件选项 Propshaft。

$ rails new --help
  -a, [--asset-pipeline=ASSET_PIPELINE]  # Choose your asset pipeline [options: sprockets (default), propshaft]
                                         # Default: sprockets

Assets Pipeline 是 Rails 从 3.1 开始内置的前端文件处理机制,它内部实现的库叫做 Sprockets。过去谈论 Assets Pipeline 跟 Sprockets 几乎不做区分,因为两者几乎重叠。而 Rails 7 开始 Assets Pipeline 提供另一个实现,那就是 Propshaft。

Propshaft 的诞生

Sprockets 是一个完整的前端构建工具,它可以对 js,css 进行编译、打包、最小化等处理,支持 CoffeeScript,Sass 编译器,还有一套自制的链接声明。如果只使用 CoffeeScript 和 Sass,并且所需的库都有对应的 gem 包,那么它的工作一直良好。

到了 Rails 7,新的前端方案是 Assets Pipeline + JS/CSSbundling,静态文件的编译、打包、最小化等处理都在 Assets Pipeline 之前进行,Pipeline 只需要做文件名哈希和提供文件名映射,这时 Sprockets 的功能就变得冗余了。

引申阅读:Rails 7 前端方案前瞻

功能的冗余有时会导致 Bug,例如已经编译好的 js 文件,再经过 Sprockets 处理会在末尾插入一个 ;,有时会导致问题(Issue #24)。目前 Sprockets 内部有大量预设的处理器,很多都用不上。理想情况下,Sprockets 应该提供配置方法关闭内置的处理器。也许是 Sprockets 的代码历史包袱太重,DHH 选择了新建一个简化版的 Sprockets——也就是现在的 Propshaft。

Propshaft 的功能

Propshaft 只做最精简的功能,包括:

  • 管理 paths:配置从哪里引用资源。
  • 计算摘要:给文件名加上哈希值,并维护文件名映射。
  • 开发服务器:开发环境提供静态文件。
  • 基础的内容处理:替换文件内的资源引用地址。

以下功能并不包含在 Propshaft 中:

  • 编译 js,css
  • 打包
  • 最小化

这些功能应该使用外部的编译工具完成。

如何替换 Sprockets

对于新项目,使用以下命令就可以自动配置 Propshaft:

$ rails new myapp -a propshaft

对于已有项目,先要确保不依赖 Sprockets 提供的编译功能,例如换用 Sass 编译 css,用 esbuild 打包 js。

引申阅读:如何从 Webpacker 切换到 CSS/JS bundling

完成 bundling 迁移后,按以下步骤迁移 propshaft:

  1. 在 Gemfile 中删除 sprockets-rails,添加 propshaft
  2. 运行 bundle
  3. 删除 asset/config/manifest.js
  4. 将 css 文件中的 image_urlasset_url 等 helper 换成 url

完成。

值得注意的变动

引用静态资源

Sprockets 在 CSS 上下文提供了一些 assets helper,例如 image_urlasset_url 来引用静态资源。在 Propshaft 中不需要这些 helper,直接用标准的 url,它会自行替换。

例如:

- background: image_url('hero.jpg');
+ background: url('hero.jpg');

在 view 中引用静态资源依然使用 image_urlasset_url 这些方法。

编译目标

Sprockets 提供了 asset/config/manifest.jsRails.application.config.assets.precompile 两个方法设置编译目标。Propshaft 不提供这个设置,而是把 assets.paths 里面的所有文件当作编译目标。

你可以用以下命令检查 Propshaft 会编译生成哪些文件:

$ rake assets:reveal

如无意外,你会发现很多不需要的文件也被当作编译目标了,例如 app/assets/stylesheets 下的 scss 源文件。

可以用以下配置排除一些目录:

Rails.application.config.assets.excluded_paths = [
  Rails.root.join("app/assets/stylesheets")
]

总结

Sprockets 对于已经使用的项目依然可以工作,为了兼容第三方 gem,Sprockets 还会维护很长时间,所以不必急着迁移。

如果项目正在往 bundling 方案迁移,那么不妨一同迁移 Propshaft,让 Assets Pipeline 的流程更精简,减少 bug。

3
2
1