<Rails> partialをrenderするときのcollectionとは?こっちの方が速いみたい。

f:id:b1840943:20180524154054p:plain

 

Railsを使ってて部分テンプレートをrenderするときにローカル変数を渡すことがあると思います。いくつか渡し方があるんですが、それぞれどう違うのか、パフォーマンスの変化についてもまとめました。

 

前提としてtweetモデルとtweetコントローラーが存在していて、以下のようにコントローラーでインスタンス変数にtweetを代入している状況とします。

 

tweets_controller
def index

  @tweets = Tweet.all

end
 

each文を使う場合

eachでlocalを渡す時のビュー

 

<% @tweets.each do |tweet| %>

 <%= render partial: "tweet", locals: { tweet: tweet } %>

<% end %>

 

@tweetsというインスタンス変数に入ってるtweetの個数分だけ部分テンプレートを表示するというロジックをeach分で実装しています。実際にtweetを表示するパーシャルは下です。

 

_tweet.html.erb

これは単純にコントローラーから渡された@tweetsに入ってる各tweetの値を表示するようにしているだけです。

 

<div class="content_post" style="background-image: url(<%= tweet.image %>);">

    <div class="more">

      <span><%= image_tag 'arrow_top.png' %></span>

      <ul class="more_list">

        <li>

          <%= link_to '詳細', "/tweets/#{tweet.id}", method: :get %>

        </li>

        <% if user_signed_in? && current_user.id == tweet.user_id %>

          <li>

            <%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %>

          </li>

          <li>

            <%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %>

          </li>

        <% end %>

      </ul>

    </div>

    <%= simple_format(tweet.text) %>

  <span class="name">

    <a href="/users/<%= tweet.user_id %>">

      <span>投稿者</span><%= tweet.user.nickname %>

    </a>

  </span>

</div>

 

さてこれで速さを測ってみました。

 

パフォーマンス

Rendered tweets/_tweet.html.erb (1.8ms)
Rendered tweets/_tweet.html.erb (0.7ms)
Rendered tweets/_tweet.html.erb (0.7ms)
Rendered tweets/_tweet.html.erb (0.6ms)

 

データベースにtweetを4つ保存していたので、@tweetsにtweetが4つ入っていました。なので4回パーシャルが呼び出されています。これは予想通りの動きです。

 

each文を使わないとき

collectionでローカル変数を渡す時のビュー

 

<%= render partial: "tweet", collection: @tweets %>

 

eachで囲まない分、こっちの方が見た目的にもすっきりしてますね。collection: @tweetsという形で渡してあげると、パーシャルでtweet.user_idといった感じでインスタンスの属性にアクセスできます。

こっちでも速度を測定してみました。

 

パフォーマンス

Rendered tweets/_tweet.html.erb (2.7ms)

 

collectionで渡すとそもそもパーシャルが一度しか呼ばれないんですね。

先ほどのeach文を使用した時は合計 (3.8ms) かかっていたので、こっちの方がパフォーマンスも良いです。

 

これで頭の中がすっきりしました。今後eachで回したいようなパーシャルがある時はcollectionを使います。