🏎️

【Rails】ViewComponentとPartialのパフォーマンスを比較

2021/04/21に公開

Railsのpartialはパフォーマンスを悪化させる、いや本番環境ではそうでもない、view_componentを使うとパフォーマンスがよくなる、いやそうでもない、、

いろんな情報があって、どれが正しいのかわからなかったので、計測してみました。

検証方法

  • 直書き、partial、partial(collectionを使った場合)、view componentで同じ内容を出力
  • 開発環境でパフォーマンス計測
  • 本番環境でパフォーマンス計測

環境構築

$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
$ rails -v
Rails 6.1.3.1
Gemfile
gem "view_component", require: "view_component/engine"
$ bundle install
$ rails g controller samples vanilla partial partial_collection view_component
$ rails g model sample name:string
$ rails db:migrate
app/controllers/samples_controller.rb
class SamplesController < ApplicationController
  before_action :build_samples
  def vanilla
  end

  def partial
  end
  
  def partial_collection
  end

  def view_component
  end
  
  def build_samples
    @samples = Sample.all
  end
end
db/seed.rb
100.times do |n|
  Sample.create!(name: "ホゲホゲ#{n}")
end
$ rails db:seed

vanilla

app/views/samples/vanilla.html.erb
<% @samples.each do |sample| %>
  <p><%= sample.name %></p>
<% end %>

partial

$ touch app/views/samples/_hoge.html.erb
app/views/samples/_hoge.html.erb
<p><%= hoge.name %></p>
app/views/samples/partial.html.erb
<% @samples.each do |sample| %>
  <%= render 'hoge', hoge: sample %>
<% end %>

partial(collection)

app/views/samples/partial.html.erb
<%= render partial: 'hoge', collection: @samples, as: 'hoge' %>

view_component

$ rails g component Hoge hoge
app/components/hoge_component.rb
class HogeComponent < ViewComponent::Base
  def initialize(hoge:)
    @hoge = hoge
  end
end
app/components/hoge_component.html.erb
<p><%= @hoge.name %></p>
app/views/samples/hoge_component.html.erb
<% @samples.each do |sample| %>
  <%= render(HogeComponent.new(hoge: sample)) %>
<% end %>

https://github.com/nyshk97/partial-view-components

計測

samples/vanillasamples/partialsamples/view_componentにアクセスし、ログの以下の箇所を確認。

ローカル

それぞれ5回ずつアクセスし、中央値を記録。

結果
vanilla 8.7ms
partial 23.3ms
partial_collection 8.5ms
view_component 9.2ms

本番

それぞれ11回ずつアクセスし、中央値を記録。
サーバーはHeroku。

結果
vanilla 5.3ms
partial 10.7ms
partial_collection 4.45ms
view_component 5.55ms

まとめ、感想

  • eachで回して使う分には、本番でもローカルでも、partialよりview_componentの方が2倍くらいパフォーマンスが良い
    • 逆に100回partialを呼んでも2倍程度しか変わらないなら、5回10回のpartialの呼び出しに神経質にならなくてもいい?
    • 今回は同じpartialを100回呼んだけど、違うパーシャルを呼ぶ場合はまた話が違うのかも
  • ローカルでも本番でも、直書きよりpartial_collectionの方がパフォーマンスが良かった。
    • 直書きの場合「何件あるか?」ってことを考えずに直列に実行するけれど、render collectionを使った場合は予め件数を数えてから、先に文字列を件数分作ってそれに必要なキーを当てはめていくから文字列生成コストの面でパフォーマンスが良いっぽい
    • https://api.rubyonrails.org/classes/ActionView/PartialRenderer.html#method-i-render
  • view_componentはeachで回してもパフォーマンスの劣化が最小限だった
    • パフォーマンスだけじゃなくて、テストが書きやすくなるなどのメリットもあるので、基本的な方針としてはview_componentを優先して使う感じが良さそう。

参考

https://zenn.dev/cobachie/articles/tried-view-component

Discussion