5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rails7をちょっと試す(turbo_frame_tag でページの表示を速くする編)

Last updated at Posted at 2022-08-21

はじめに

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つ作る

UserBook の2つの model を作ります。

bin/rails g model User name
bin/rails g model Book title

seed データを作る

UserBook それぞれ1つずつデータを作ります。

db/seeds.rb
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_allBook.long_time_all はわざと時間がかかるようにこの後、model側で実装します。

app/controllers/dashboard_controller.rb
  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秒かかるように見せかけます。

app/models/user.rb
  scope :long_time_all, lambda {
    sleep 1
    all
  }
app/models/book.rb
  scope :long_time_all, lambda {
    sleep 2
    all
  }

View を作成する

User と Book の両方のデータを表示する View を作成します。

Dashboard のページ

Dashboard#index の実行開始時刻を表示します。
User と Book の表示は、 partial を使います。

最後に現在時刻を表示します。

app/views/dashboard/index.html.erb
<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については省略)

app/views/dashboard/_users.html.erb
<% @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秒かかって表示される計算です。

before01_000.png
上のスクリーンショットから以下がわかります。

  • 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秒かかっていることがわかります。
before02.png

ここからが本番

ここからが本番です。
Rails7 から使えるようになった turbo_frame_tag を使ってページの表示を速くしてみましょう。
User と Book のデータの表示する部分を turbo_frame_tag を使って書き換えて速度を改善してみます。

ルーティングを追加する

User の表示と Book の表示を非同期にするため、 User と Book のデータを取得するためのルーティングを追加します。

config/routes.rb
  get 'dashboard/users'
  get 'dashboard/books'

Dashboard#users と Dashboard#books コントローラを追加する

Dashboard#index から User と Book のデータ取得する処理を削除し、別に usersbooks メソッドを追加します。

app/controllers/dashboard_controller.rb
  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 のデータの表示も同様に変更します。

app/views/dashboard/index.html.erb
<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.erbturbo_frame_tag の引数で指定したのと同じ :usersにします。同じにすることで、 app/views/dashboard/index.html.erbturbo_frame_tag で囲った部分がレスポンスで差し替えられます。
turbo_frame_tag のblockの中身は既存の app/views/dashboard/_users.html.erb を利用します。

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秒に短縮されました。

turbo_frame.gif
上のアニメーションgifから以下がわかります。

  • 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秒短縮できました。
after01.png

まとめ

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 の機能については色々と今後も触っていきたいと考えています。

参考情報

今回試したコードは以下で確認できます。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?