Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: Turbo StreamsとTurbo Framesの使い分けを理解する(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: Turbo StreamsとTurbo Framesの使い分けを理解する(翻訳)

Rails Doctrineの第1条にある「プログラマーの幸せのために最適化すること(Optimize for programmer happiness)」は、おそらくRuby on Railsが成功した要因であると個人的に考えています。さらにいえば、Railsの最適化はプログラマー以外の人も対象にしていると言ってよいでしょう(「プログラマーでない人々」がRailsを活用してオンラインビジネスを立ち上げたという話が知られています)。そのかなりの部分が、Railsの「設定より規約(Convention over Configuration: CoC)」のおかげで可能になっています。これについて異論があるかもしれませんが、実際にそうなのです。

「プログラマーの幸せのために最適化すること」は、最終的に次のように説明されています。「幸せのために最適化することは、おそらくRuby on Railsが形成されるうえで最も重要な鍵であり、今後もそうあり続けるでしょう。」これはまさにその通りで、現在もHotwireでこのアイデアを目にすることが可能です。

まずはTurbo Driveから始めてみましょう。Turbo Driveは変更は不要ですぐ使えるようになっており、ページを再読み込みせずに<body>を差し替えられるようになります。コストなしで使えることは、確実に幸せな気持ちを高めてくれます。

しかしHotwireには、Turbo FramesとTurbo Streamsという切れ味のよい刃物もあります。私は37signalsの記事にある以下の図が大好きです。


出典: https://dev.37signals.com/a-happier-happy-path-in-turbo-with-morphing/

しかし私はこの図について少しばかり異論があります。この図によればTurbo FramesやTurbo Streamsを使ったときの嬉しみは(Turbo Driveによる)bodyの差し替え機能に及ばないことになりますが、それでも私はTurbo FramesやTurbo Streamsを使っていてとても興奮します。実を言えば、私はむしろTurbo FramesとTurbo Streamsの方が好きなのです。ページを正確に変更できますし、バックエンドの計算量やレスポンスのサイズも少なくて済みます。
とは言うものの、やはりTurbo FramesとTurbo Streamsには違いがあるので、本記事では両者の違いを示すことで、皆さんが実際のケースに応じて慎重に使えるようにしたいと思います。

🔗 Turbo Frames

最初はTurbo Framesを見ていきましょう。Turbo Framesは、ページを複数のパーツに切り分けるときに便利です。

Turbo Framesで最も重要な点は、置き換えられるのは1個のフレームのみであるということです(デフォルトでは「HTTPリクエストを行う」フレームが対象です)。

projectsというTurbo Frameの「中に」ボタンが置かれている場合、リクエストの送信後にサーバーが返すレスポンスに2つ以上のフレームが含まれていても、projectsフレームだけが更新されます。Turbo-Frameリクエストヘッダーを調べてみると、差し替え対象となるフレーム名が表示されていることがわかります。

コンテンツを更新したいフレームの「外に」ボタンが置かれている場合でも、フレームの更新をトリガーできますが、その場合は以下のように更新対象となるフレームを明示的に指定する必要があります。

button_to "Delete", @projects.first, method: :delete, data: {turbo_frame: "projects"}

つまり、Turbo Frameを使うのであれば、常に「既存のフレームのコンテンツを新しいものに差し替える」ことだけに用いるということです。

🔗 Turbo Streams

さて、実は最も紛らわしいのはTurbo Streamsの部分です。

  1. Turbo Streamsでは、WebSocketは必須ではありません
    Turbo Streamsは、「通常のWebリクエスト」「WebSocket」「SSE(Server Sent Events)」で利用できます。

  2. Turbo Streamによるストリーミングは、「WebSocket上でデータをブロードキャストすること」とみなせます。
    このフォーマットはAcceptヘッダーとContent-Typeヘッダーでも使われる(MIMEタイプはtext/vnd.turbo-stream.html)ので、サーバーサイドでコンテンツを配信するときにも利用できます。

respond_to do |format|
  format.html { redirect_to projects_url }
  format.turbo_stream do
    render turbo_stream: turbo_stream.remove(dom_id_for(@project))
  end
end

Turbo Streamsは、1件のレスポンスで複数のDOM要素を操作できる点がTurbo Framesと異なります。
また、Turbo Streamsでは、要素の単なる差し替え(replace)の他にも、appendprependupdateremoveなどが使えます。

format.turbo_stream do
  render turbo_stream: [
    turbo_stream.prepend('ongoing_projects', partial: 'projects/kanban/ongoing_project'),
    turbo_stream.remove(dom_id_for(@project))
  ]
end

🔗 Turbo FramesとTurbo Streamsをどう使い分けるか

最も大事な点を以下にまとめました。

  • Turbo Frames:
    • 差し替え可能なのは1個のフレームだけなので、レスポンスで複数のフレームを返しても無効です。
    • デフォルトでは、HTTPリクエストを送信するフレームが差し替え対象になります。
      フレームの外側にあるフレームを置き換え対象にする場合は、フレーム名を指定する必要があります。
    • 本質的にフレームの差し替えだけを行います。
  • Turbo Streams:
    • WebSocketは必須ではありません。
      ただしWebSocketを使えば、すべての関係者に更新をリアルタイムでブロードキャスト可能になります。
    • MIMEタイプはtext/vnd.turbo-stream.htmlです。
    • Turbo Framesと異なり、1回のレスポンスで、互いに無関係な複数のページ要素を操作可能です。
    • 単なる差し替え以外に、appendprepend、削除などもサポートしています。

両者を表形式で比較してみました。

機能 Turbo Frames Turbo Streams
更新対象 単一要素のみ 複数要素
更新の種類 差し替えのみ append, prepend, replace, update, remove, before, after, refresh
リアルタイム更新 不可 WebSockets経由で可能だが、単なるボーナス

Turbo StreamsとTurbo Framesのどちらを使うか悩んだときにこれが役立ちます。質問がありましたらお気軽にメールにてお問い合わせください。

それではまた!

追伸: Hotwire関連の記事をもっとお読みになりたい方は、以下の動画や記事もどうぞ。

関連記事

Rails: HotwireのTurbo Streamsとリダイレクトのどちらを使うか?(翻訳)

Rails: '読み込み中'アニメーションをTailwindCSSでTurbo Framesに追加する方法(翻訳)

Rails: TurboとViewComponentを使うときの注意点(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。