はじめに
Rails 7.0.0 には、 Turbo が組み込まれています。
今回はその中で、 turbo_frame_tag
を試してみたいと思います。
turbo_frame_tag
を使ってページの表示速度を改善してみます。
以下のバージョンで確認しました。
- Ruby 3.0.2
- Rails 7.0.3.1
前準備をする
今回は前準備に少し手間がかかります。
Rails プロジェクトを作る
新しくRailsプロジェクトを作ります。このとき `
(データベースオプションを指定しているのに深い意味はありません。)
mkdir rails_sandbox
cd rails_sandbox
rails new . --database=postgresql
model を2つ作る
User
と Book
の2つの model を作ります。
bin/rails g model User name
bin/rails g model Book title
seed データを作る
User
と Book
それぞれ1つずつデータを作ります。
User.create(name: 'Taro')
Book.create(title: 'Ruby on Rails')
データベースを作成して、seed データを登録する
データベースを作成して、seedデータを登録します。
bin/rails db:create db:migrate db:seed
Dashboard コントローラと index View を作成する
User と Book の両方のデータを表示するためのコントローラとViewを作ります。
bin/rails g controller Dashboard index
Dashboard#index メソッドを実装する
Dashboard#index
を実装します。
Dashboard#index
の実行が開始された時刻を画面に表示するため、 @index_at
に現在時刻を設定します。
User.long_time_all
と Book.long_time_all
はわざと時間がかかるようにこの後、model側で実装します。
def index
@index_at = Time.zone.now
@users = User.long_time_all
@books = Book.long_time_all
end
long_time_all をscopeで定義する
scope
を使って2つのmodelの long_time_all
を定義します。
sleep を使ってデータの検索の時間がかかるように見せかけます。
User と Book で sleep の時間を変えておきます。
Userは検索に1秒かかり、Bookは2秒かかるように見せかけます。
scope :long_time_all, lambda {
sleep 1
all
}
scope :long_time_all, lambda {
sleep 2
all
}
View を作成する
User と Book の両方のデータを表示する View を作成します。
Dashboard のページ
Dashboard#index
の実行開始時刻を表示します。
User と Book の表示は、 partial を使います。
最後に現在時刻を表示します。
<h1>Dashboard#index</h1>
<p>index controller start at <%= @index_at %></p>
<h2>Users</h2>
<%= render partial: 'users' %>
<h2>Books</h2>
<%= render partial: 'books' %>
<p>rendered index at <%= Time.zone.now %></p>
User と Book データの View
単純に user.name
を表示します。
最後に現在時刻を表示するようにします。
Book についても同様に実装します(Bookについては省略)
<% @users.each do |user| %>
<p>
<%= user.name %>
</p>
<% end %>
rendered users at <%= Time.zone.now %>
表示してみる
Rails サーバーを bin/rails s
で起動して、 http://localhost:3000/dashboard/index にアクセスします。
だいたい3秒後にページが表示されます。
Userの検索に1秒、Bookの検索に2秒かかるので合計で大体約3秒かかって表示される計算です。
- controller の開始が 05:22:08
- Usersのrender が 05:22:11
- Booksのrender が 05:22:11
- ページ全体のrenderが 05:22:11
つまり、controllerの開始(05:22:08)からページ全体のrender(05:22:11)まで3秒経過しています。
次のスクリーンショットを見れば、最初に index の読み込みに3.09秒かかっていることがわかります。
ここからが本番
ここからが本番です。
Rails7 から使えるようになった turbo_frame_tag
を使ってページの表示を速くしてみましょう。
User と Book のデータの表示する部分を turbo_frame_tag
を使って書き換えて速度を改善してみます。
ルーティングを追加する
User の表示と Book の表示を非同期にするため、 User と Book のデータを取得するためのルーティングを追加します。
get 'dashboard/users'
get 'dashboard/books'
Dashboard#users と Dashboard#books コントローラを追加する
Dashboard#index
から User と Book のデータ取得する処理を削除し、別に users
と books
メソッドを追加します。
def index
@index_at = Time.zone.now
end
def users
@users = User.long_time_all
end
def books
@books = Book.long_time_all
end
View を変更する
dashboard/index のView の変更
dashboard/index ページの User のデータの表示箇所を変更します。
turbo_frame_tag
を使って /dashboard/users
に GET でアクセスし、そのレスポンスを表示するように変更します。
レスポンスが返されるまでは、 loading...
と表示するようにします。
Book のデータの表示も同様に変更します。
<h2>Users</h2>
<%= turbo_frame_tag :users, src:'/dashboard/users' do %>
loading...
<% end %>
<h2>Books</h2>
<%= turbo_frame_tag :books, src:'/dashboard/books' do %>
loading...
<% end %>
Dashboard#users Dashboard#books が返す response のテンプレートを作成する
Dashboard#users
が返すレスポンスで使用するテンプレートを作成します。
turbo_frame_tag
を使います。
turbo_frame_tag
の引数は、 app/views/dashboard/index.html.erb
の turbo_frame_tag
の引数で指定したのと同じ :users
にします。同じにすることで、 app/views/dashboard/index.html.erb
の turbo_frame_tag
で囲った部分がレスポンスで差し替えられます。
turbo_frame_tag
のblockの中身は既存の app/views/dashboard/_users.html.erb
を利用します。
<%= turbo_frame_tag :users do %>
<%= render partial: 'dashboard/users', collection: @users %>
<% end %>
Dashboard#books
が返すレスポンスで使用するViewのテンプレートも同様に作成します。
再度アクセスする
http://localhost:3000/dashboard/index にアクセスしてみましょう。
今度はページがすぐに表示されて、Users 部分が1秒後に、Book が2秒後に表示されるのがわかります。
3秒かかっていたページの表示が2秒に短縮されました。
- controller の開始が 04:50:49
- Usersのrender が 04:50:50
- Booksのrender が 04:50:51
- ページ全体のrenderが 04:50:49
つまり、controllerの開始(05:22:08)からページ全体が表示されるまで(04:50:51)まで2秒経過しています。
次のスクリーンショットを見れば、最初のindexの読み込みは、20msで終わり、users が1.01秒、booksが2.01秒かかっていることがわかります。users と books の読み込みは非同期に実行されるため、結果的におよそ1秒短縮できました。
まとめ
Turbo をうまく活用することでページの表示を速くできました。
従来であれば、JavaScriptでfetchして、 User と Book のデータをAPI経由で json 形式で受け取って、ページの一部を JavaScript で書き換えるという方法になると思います。
ですが、ここでは、JavaScript を1行も書かずに(裏ではJavaScriptが動いていますが、)同じことを実現できているところがポイントです。JavaScriptを使わない Rails によるWebアプリが今後増えていくのかどうか気になるところです。
補足事項
今回は、 手抜きで 話を簡単にするために ルーティングを dashboard/users と dashboard/books にしましたが、本来は、 dashboard/users/index や dashboard/books/index にするとか、users/index と books/index にするとか、それぞれのリソースに合わせて REST にするのが Rails way なやり方かも知れません。
Hotwire の機能については色々と今後も触っていきたいと考えています。
参考情報
今回試したコードは以下で確認できます。