🌊

Railsのstylesheet_link_tagとstylehseet_pack_tagの違いや使い所ついて調べてみた

2021/07/10に公開

株式会社TECH LUCKという会社で代表兼エンジニアをしている齊藤です。

DXプロジェクト、開発プロジェクト、Rails開発などでお困りごとがありましたら弊社HPからご相談をいただけますと幸いです。
以下のような問題に対応することが可能です。

  • プロジェクトでRailsエンジニアが足りなくて困っている
  • Railsのバージョンアップをしたいがノウハウ・リソースが足りなくて困っている
  • オフショア開発をしているが、要件の齟齬やコード品質が悪いので改善したい

また、Railsエンジニアも募集しておりますので、興味がありましたら弊社HPからご連絡いただけますと幸いです。

前提

Railsのstylehseet_pack_tagstylesheet_link_tagの違いや使い所がよくわからなかったので、色々と試しながらまとめた記事になります。

一言でまとめると

stylesheet_link_tagは、CSSをこれまでのRailsでの管理方法(Sprockets)で管理する際に記述する。つまり、CSSをapp/assets/stylesheets内で管理する際に必要。

stylehseet_pack_tagは、CSSをWebpackerで管理する際に記述する。つまり、CSSをapp/javascripts/stylesheetsで管理する際に必要。

デフォルト設定の場合

Ruby on Railsのv6系を指定し、rails new _6.0.0_ <アプリ名> -d mysqlした直後のプロジェクトのapp/views/layouts/application.html.erbはデフォルトで以下のようになっています。

application.html.erb
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

このデフォルトの状態で正常にCSSなどのファイルが読み込まれるのか確認するため以下の設定をしました。

  • app/javascript/packs/test.jsを作成し、console.log('test')を記述
  • app/javascript/packs/appliction.jsに、imoprt 'packs/test'を追記
  • app/assets/stylesheets/test.cssを作成し、body{color: red;}を記述

app/assets/stylesheets/application.css には *= require_tree .が記述されており、app/assets/stylesheetsのディレクトリ配下のCSSファイルは自動的に読み込まれますが、app/javascript/packs/application.js にはそのような記述がないため、都度ファイルを読み込むための記述をします。(ディレクトリ以下を読み込む記述があれば教えていただきたいです。)

上記のように編集して、developmentモード、productionモードの各々の設定でrailsサーバーを起動してもエラーは起きず、追記したCSS・JSが反映された画面がエラーなく正常に表示されます。

productionモードの設定で起動するには以下の記事が参考になります。
https://qiita.com/mokuo/items/207144d6bb18967a9f62

※NginxなどのWebサーバーなどを使っていない場合には、config/environments/production.rbの設定を以下のように変更します。

production.rb
config.public_file_server.enabled = true

こちらの設定の詳細の中身は以下の記事に書かれていますのでご参考までに。
https://qiita.com/at-946/items/b7d467bf25c40fcfca44

stylesheet_pack_tagがproduction環境でエラーが出る

デフォルト設定に以下のように stylesheet_pack_tag を追記して、developmentモードでサーバーを起動しても特にエラーは起きませんでした。

application.html.erb
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

しかし、この設定のままproductionモードでサーバーを起動するとエラーが起きてしまいます。つまり、開発時には特にエラーは起きていないのに、本番リリース時にはエラーが起きてしまうということになります。

エラーについては以下のログが吐き出されていました。

F, [2021-07-09T19:31:22.706284 #66663] FATAL -- : [e424266f-a5da-4358-b924-5cc0cbcb9258]   
[e424266f-a5da-4358-b924-5cc0cbcb9258] ActionView::Template::Error (Webpacker can't find application.css in /Users/saitouryouzi/developer/jeans/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-c3e3aae48c4c321181e9.js",
  "application.js.map": "/packs/js/application-c3e3aae48c4c321181e9.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-c3e3aae48c4c321181e9.js"
      ],
      "js.map": [
        "/packs/js/application-c3e3aae48c4c321181e9.js.map"
      ]
    }
  }
}
):
[e424266f-a5da-4358-b924-5cc0cbcb9258]      7:     <%= csp_meta_tag %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]      8: 
[e424266f-a5da-4358-b924-5cc0cbcb9258]      9:     <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     10:     <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     11:     <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     12:   </head>
[e424266f-a5da-4358-b924-5cc0cbcb9258]     13: 
[e424266f-a5da-4358-b924-5cc0cbcb9258]   
[e424266f-a5da-4358-b924-5cc0cbcb9258] app/views/layouts/application.html.erb:10

開発(developmentモード)時にはエラーが起きなくて、本番(productionモード)時にはエラーが起きる理由は以下の記事に詳細が書いてありました。
https://qiita.com/kazutosato/items/23d2aa126084d054398b

javascript_pack_tag と stylesheet_pack_tag の仕様はこうなってます:development環境では、JavaScriptを使って動的にCSSを埋め込む。CSSファイルを生成してlinkタグに指定することはしない。

つまり、javascript_pack_tagstylesheet_pack_tagはdevelopmentモードではlinkなどは生成していない。しかし、productionモードではlinkを生成する形になり、その参照先が何もないためエラーが発生するようです。
もっと正確に記述すると、config/webpacker.ymlの中でextract_css: trueの場合にはlinkを出力し、extract_css: falseの場合にはJavaScriptを使って動的にCSSを読み込むという動作になります。

GitHubでstylesheet_pack_tagの実装を見てみると、そのようなコメントがありました。
https://github.com/rails/webpacker/blob/d456b1b3783313bf0b3927bed6f434d1de5854c4/lib/webpacker/helper.rb#L131-L147

When extract_css is false in webpacker.yml:
<%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => nil
When extract_css is true in webpacker.yml:
<%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => <link rel="stylesheet" media="screen" href="/packs/calendar-1016838bab065ae1e122.css" data-turbolinks-track="reload" />

実際に、config/webpacker.ymlの中身を見てみると以下のようになっています。(長いので関係ない箇所は省略しました。)

webpacker.yml
# Note: You must restart bin/webpack-dev-server for changes to take effect

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs
  cache_path: tmp/cache/webpacker
  webpack_compile_output: true
  additional_paths: []
  cache_manifest: false
  extract_css: false
  static_assets_extensions:
    ...
  extensions:
    ...

development:
  <<: *default
  compile: true

production:
  <<: *default
  compile: false
  extract_css: true
  cache_manifest: true

development環境では、extract_css: falseとなっており、production環境では、extract_css: trueとなっています。
そのため、development環境ではJavaScriptで動的にCSSを読み込む形になっていました。

試しに、config/webpacker.ymlのdeveloment環境のextract_css: trueに設定して、ailsサーバーをdevelopmentモードで動かすと、さきほどproduction環境で出たエラーと同じエラーが発生しました。

stylesheet_pack_tagの使い所

stylesheet_pack_tagの使いどころは、JavaScriptだけでなくCSSもWebpackerで管理したい時になります。(個人的な考えとして、Railsの標準ではそのような設定ではないので、極力CSSはWebpackerで扱わない方がいいのではないかと思っています。)

https://techracho.bpsinc.jp/hachi8833/2021_05_06/83678

WebpackerでCSSを管理するのに必要な設定は以下の通りです。

  • stylesheet_link_tagstylesheet_pack_tagに変更する
  • app/javascript/application.jsに、利用したいCSSファイルをインポートする

今回はテストのために、app/javascript/stylesheets/test.cssを作成して、app/javascript/application.jsimport 'stylesheets/test'を記述してCSSが読み込まれるかテストしました。

application.html.erb
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
test.css
body{
  color: blue;
}
application.js
import 'stylesheets/test'

このようにして、developmentモード(config/webpacker.ymlのdevelopment環境はextract_css: falseに設定)でrailsサーバーを起動すると、エラーなどは起きずにCSSが正常に読み込まれていました。
これと全く同じ形でproductionモード(config/webpacker.ymlのdevelopment環境はextract_css: trueに設定)でrailsサーバーを起動してみても、エラーなどは起きずにCSSが正常に読み込まれていました。

参考記事

https://techracho.bpsinc.jp/hachi8833/2021_05_13/85940
https://techracho.bpsinc.jp/hachi8833/2021_06_10/85943
https://qiita.com/chimame/items/8d3d6f4afea675cffa7d
https://www.bokukoko.info/entry/2020/02/05/165223

Discussion