LoginSignup
35
24

More than 5 years have passed since last update.

content_forとprovideの違い

Last updated at Posted at 2017-10-22

ちょっと調べたのでメモ。

content_forprovide の違い

結論から言うと、デフォルトでは両者に違いはないようです。
ただし、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の処理順序

  1. reanderメソッド呼び出し
  2. template(app/view/plans/show.html.slimとか)のrender
  3. layout(app/view/layouts/application.html.slimとか)のrender

では、Streaming利用時にRailsのviewの処理順序はどのようになるのでしょうか?

Streaming時におけるviewの処理順序

  1. renderメソッドの呼び出し
  2. layout(app/view/layouts/application.html.slimとか)のrender
  3. template(app/view/plans/show.html.slimとか)のrender

通常時とStreaming利用時ではlayoutとtemplateのrenderの順序が違うことがわかります。このように、Streaming利用時は先にlayoutをrenderしてlayoutのheader部分をクライアントに送ることで、クライアントがtemplateのreanderを待っている間、JSやCSSを先にロードしておくことができるようになります。

content_forprovideの違い

やっと本題に移ります。Streaming時に、content_forprovideはどのような違いが生まれるのでしょうか?

ストリーミング時のcontent_forの挙動

通常時のcontent_forと変わりません。そもそもcontent_forは、どういうものなのでしょうか?RailsGuideを見てみると以下のように説明されています。

content_forメソッドを使用することで、コンテンツを名前付きのyieldブロックとしてレイアウトに挿入できます。

すなわち

app/views/hoge/show.html.slim
- content_for :title, 'hogehoge'

とすることで、以下のlayout側のyield(:title)hogehgoeが入ります。

app/view/layouts/application.html.slim
html
  head
    title  = yield(:title) # <- 'hogehoge' が入る
  body
    yield

また、このcontent_forは同じ名前を持つものに対し複数回適用することが可能であり、その結果は単純に繋げられていきます。すなわち、

app/views/hoge/show.html.slim
- content_for :title, 'hogehoge'
- content_for :title, ', ほげほげ'

とすると、layout側のyield(:title)には、hogehoge, ほげほげを繋げた結果hogehoge, ほげほげが入ります。

app/view/layouts/application.html.slim
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回呼び出すと以下のようになります。

app/views/hoge/show.html.slim
- provide :title, 'hogehoge'
- provide :title, ', ほげほげ'
app/view/layouts/application.html.slim
html
  head
    title = yield(:title) # <- 'hogehoge' が入る
  body
    yield

このようにすることで、早い段階でlayout内のheadのtitleの内容が定まるので、ヘッダー部分をより早くクライアントに送信できるようになり、Streamingの恩恵を受けられるようになります。

結局、content_forprovideのどっちを使えばよいのか?

以上のことから結論付けると、基本は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

35
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
24