解决 Rails 7 下 Sass 引用 Font Awesome 的问题

Rei
·

问题

Rails 7 引入了新的前端方案 CSS/JS bundling,让开发者更容易选择适合自己的打包工具。不过新方案要求开发者对 Assets Pipeline 的工作原理有更深的了解,不然会出现各种状况。

例如在引用 Font Awesome 的时候,可能会发生编译了 CSS,但没有编译字体文件的情况,这要怎么解决呢?

解决

假设项目已经配置了 Sass 作为 CSS bundling 方案。

参考 如何从 Webpacker 切换到 CSS/JS bundling

首先安装 Font Awesome:

$ yarn add @fortawesome/fontawesome-free

然后在 config/initializers/assets.rb 添加以下内容:

Rails.application.config.assets.paths << Rails.root.join('node_modules/@fortawesome/fontawesome-free/webfonts')

最后在 app/assets/stylesheets/application.sass.scss 添加以下内容:

$fa-font-path: ".";

@import "@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import "@fortawesome/fontawesome-free/scss/solid.scss";

以上就是引用 Font Awesome 所需的配置.

讨论

如果只是拷贝配置,可能并不明白它工作的原理,下次遇到别的库出现问题还是无法解决。所以接下来解释它的原理。

首先要了解,Assets Pipeline + CSS Bundling 的流程中,CSS 会经历以下处理:

  • Sass 将 .sass 编译成 .css
  • Assets Pipeline 处理 .css,将里面 url() 引用的资源路径替换成包含哈希值的路径。
  • 将处理后的 .css 文件加上哈希值,储存在资源列表中,供 layout 引用。

假设在 app/assets/stylesheets/application.sass.scss 只有以下内容:

@import "@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import "@fortawesome/fontawesome-free/scss/solid.scss";

Sass 会把该文件编译到 app/assets/builds/application.css,其中包含以下内容:

@font-face {
  font-family: "Font Awesome 6 Free";
  font-style: normal;
  font-weight: 900;
  font-display: block;
  src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype");
}

可以看到,Font Awesome 的 CSS 内容已经编译成功了,但其中字体的 url() 指向的资源并不在 Assets Piepeline 的资源列表中,Assets Pipeline 会保留 url() 原本模样不做处理。最终导致浏览器去下载 ../webfonts/fa-solid-900.woff2, 得到 404 错误,字体无法呈现。

要解决这个问题,首先我们要把字体文件添加到 Assets Pipeline 的引用路径中,这就是以下这行代码的作用:

Rails.application.config.assets.paths << Rails.root.join('node_modules/@fortawesome/fontawesome-free/webfonts')

添加这行代码后,Assets Pipeline 的资源列表中就能找到 fa-solid-900.woff2 这个文件。

另一个问题是,Font Awesome 的默认字体路径处于 ../webfonts/ 目录,依然跟资源地址不匹配。所以需要加上以下配置:

$fa-font-path: ".";

这个变量是 Font Awesome 提供,用来修改字体地址的。修改变量后,编译后的 CSS 内容为:

@font-face {
  font-family: "Font Awesome 6 Free";
  font-style: normal;
  font-weight: 900;
  font-display: block;
  src: url("./fa-solid-900.woff2") format("woff2"), url("./fa-solid-900.ttf") format("truetype");
}

由于 application.cssfa-solid-900.woff2 在 Assets Pipeline 的路径中都处于根目录,这样就能找到对应的资源。

如果在浏览器中查看最终的 CSS,还会看到字体地址已经加上了哈希值:

@font-face {
  font-family: "Font Awesome 6 Free";
  font-style: normal;
  font-weight: 900;
  font-display: block;
  src: url(/assets/fa-solid-900-addc97d14257b43232b89194f73bd3b862007d5eedcb4569362b8f26356d8db3.woff2) format("woff2"), url(/assets/fa-solid-900-a0cc38b88839387e4451bb1ebdd9ecd821b2df0f7fcd5b26df75630f4171ee32.ttf) format("truetype");
}

总结

除了 Font Awesome,其他需要引用字体、图片的前端库,在 Rails 7 的打包方案下也可能遇到以上问题。如果是资源地址不正确、没有编译相关文件的问题,可以参照本文的方法解决。

评论

这两个问题不一样,这里解决的是 css 里面的字体引用问题,字体引用是 sprockets 处理的。

scss 的 load path 是在 scss 命令处理的,只要编译的时候加上 --load-path=node_modules 就可以引用 node_modules 里的 scss。

完整命令:

sass app/assets/stylesheets/application.scss app/assets/builds/application.css --no-source-map --load-path=node_modules

从头配置 css bundling 的方法可以看这里 如何从 Webpacker 切换到 CSS/JS bundling

reply
回复
1 回复
arrow_right_alt

我用yarn添加的插件css文件都不能直接在application.scss里面import, 原来是需要修改asset path, 如果每个都添加也太麻烦了吧! 我都是直接复制到app/assets/stylesheets目录下的.

reply
回复
1 回复
arrow_right_alt
社区准则 博客 联系 反馈 状态
主题