ちょっと調べたのでメモ。
content_for
と provide
の違い
結論から言うと、デフォルトでは両者に違いはないようです。
ただし、RailsのStreamingを利用している場合に違いが出てきます。
Streamingとは
かなりざっくり説明するとviewのレンダリングの順番を変えることで、ページのヘッダーを先に送信し、その後(レンダリングに時間のかかる)ボディーの部分を別で送信する事を可能にします。このようにすることで、ボディーの受信を待っている間、クライアントはヘッダー部分にあるJSやCSSを先にロードすることができるようになり、その結果表示速度が早くなります。
では、どのような時にStreamningが有効なのでしょうか?
多くの場合、以下のような重いクエリを投げる時にStreamingを使います。
class PostsController
def index
@posts = Post.all
@articles = Article.all
@plans = Plan.all
render stream: true
end
end
この時、render stream: true
とすることでStreamingを有効にすることができます。
Streaming時におけるviewの処理順序の変化
Streaming利用時にRailsの挙動はどのように変化するのでしょうか?まずは、通常時のRailsにおけるviewの処理の順序を説明します。
通常時におけるviewの処理順序
-
reander
メソッド呼び出し - template(
app/view/plans/show.html.slim
とか)のrender - layout(
app/view/layouts/application.html.slim
とか)のrender
では、Streaming利用時にRailsのviewの処理順序はどのようになるのでしょうか?
Streaming時におけるviewの処理順序
-
render
メソッドの呼び出し - layout(
app/view/layouts/application.html.slim
とか)のrender - template(
app/view/plans/show.html.slim
とか)のrender
通常時とStreaming利用時ではlayoutとtemplateのrenderの順序が違うことがわかります。このように、Streaming利用時は先にlayoutをrenderしてlayoutのheader部分をクライアントに送ることで、クライアントがtemplateのreanderを待っている間、JSやCSSを先にロードしておくことができるようになります。
content_for
とprovide
の違い
やっと本題に移ります。Streaming時に、content_for
とprovide
はどのような違いが生まれるのでしょうか?
ストリーミング時のcontent_for
の挙動
通常時のcontent_for
と変わりません。そもそもcontent_for
は、どういうものなのでしょうか?RailsGuideを見てみると以下のように説明されています。
content_forメソッドを使用することで、コンテンツを名前付きのyieldブロックとしてレイアウトに挿入できます。
すなわち
- content_for :title, 'hogehoge'
とすることで、以下のlayout側のyield(:title)
にhogehgoe
が入ります。
html
head
title = yield(:title) # <- 'hogehoge' が入る
body
yield
また、このcontent_for
は同じ名前を持つものに対し複数回適用することが可能であり、その結果は単純に繋げられていきます。すなわち、
- content_for :title, 'hogehoge'
- content_for :title, ', ほげほげ'
とすると、layout側のyield(:title)
には、hogehoge
と, ほげほげ
を繋げた結果hogehoge, ほげほげ
が入ります。
html
head
title = yield(:title) # <- 'hogehoge, ほげほげ' が入る
body
yield
ただ、content_for
には問題があります。content_for
利用時は全てのcontent_for
を見る必要があるため、template全てを読み込む必要があります。結果、通常時のviewの処理順序と変わりがなく、Streamingの恩恵を受けられません。では、どうすればこの問題を解決できるのでしょうか?ここで、provide
の出番です。
ストリーミング時のprovide
の挙動
通常時はcontent_for
と変わりません。ですが、Streaming時は一番最初の呼び出しで名前付きのコンテンツを決定してしまいます。そのため、content_for
と同じようにprovide
を2回呼び出すと以下のようになります。
- provide :title, 'hogehoge'
- provide :title, ', ほげほげ'
html
head
title = yield(:title) # <- 'hogehoge' が入る
body
yield
このようにすることで、早い段階でlayout内のheadのtitleの内容が定まるので、ヘッダー部分をより早くクライアントに送信できるようになり、Streamingの恩恵を受けられるようになります。
結局、content_for
とprovide
のどっちを使えばよいのか?
以上のことから結論付けると、基本はcontent_for
を使うのが良いかと思います。逆に、(複数回呼び出される特殊な事がない限り)Streaming時はprovide
を使うようにしないといけません。
ざっと調べて書いたので何か間違いや分かりにくい所、誤字脱字等あれば、コメント頂ければと思います。
参考
http://api.rubyonrails.org/v5.1/classes/ActionController/Streaming.html
https://stackoverflow.com/questions/27814500/ruby-on-rails-provide-vs-content-for
http://railscasts.com/episodes/266-http-streaming?language=ja&view=asciicast