Turbo 1: はじめに

:::note
この記事は[Google翻訳](https://translate.google.co.jp/)の結果を編集したものです。
:::

https://turbo.hotwired.dev/handbook/introduction

TurboにはJavaScriptをあまり使用せずに高速かつ現代的で漸進的に強化されたWebアプリケーションを作成するためのいくつかの手法がバンドルされています。これはすべてのロジックをフロントエンドに配置してアプリのサーバーサイドをJSON APIくらいにとどめる流行のクライアントサイドフレームワークのより単純な代替手段を提供します。

TurboではサーバーにHTMLを直接配信させることができます。つまり、アクセス許可を確認するためのすべてのロジック、ドメインモデルと直接対話すること、およびアプリケーションのプログラミングに必要なその他すべてのことをお好みのプログラミング言語内で多かれ少なかれ排他的に行うことができます。もうJSON分割の両側でロジックをミラーリングしなくなります。すべてのロジックはサーバー上にあり、ブラウザーは最終的なHTMLのみを処理します。

このHTML-over-the-wireアプローチの利点については[Hotwireサイト](https://hotwired.dev/)で詳しく読むことができます。以下はこれを可能にするためにTurboがもたらす技術です。

# Turbo Drive: 永続的なプロセス内を移動する

旧式の個別ページアプローチと比較した場合、従来のシングルページアプリケーションの主な魅力はナビゲーションの速度です。SPAはアプリケーションプロセスを常に破棄するのではなく、次のページで再初期化するだけでその速度を大幅に向上させます。

Turbo Driveは同じ永続プロセスモデルを使用することで同じ速度を実現しますが、パラダイムに沿ってアプリケーション全体を作成する必要はありません。クライアント側のルーターを維持する必要はなく、慎重に管理する必要のある状態もありません。永続的なプロセスはTurboによって管理され、サーバーサイドのコードは2000年代初頭に生きているかのように記述でき、今日のSPAの怪物の複雑さから幸いなことに隔離されています。

これは同じドメインへの`<a href>`リンクのすべてのクリックを捕捉することによって発生します。適格なリンクをクリックするとTurbo Driveはブラウザーがそのリンクをたどることを妨げ、 [History API](https://developer.mozilla.org/en-US/docs/Web/API/History)を使用してブラウザーのURLを変更し、[fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch)を使用して新しいページを要求して、それからHTMLのレスポンスをレンダリングします。

フォームも同様に扱います。それらの送信はTurbo Driveがリダイレクトに従ってHTMLのレスポンスをレンダリングする`fetch`リクエストに変換されます。

レンダリングをしている間、Turbo Driveは現在の`<body>`要素を完全に置き換えて`<head>`要素の内容をマージします。JavaScriptウィンドウとドキュメントオブジェクト、および`<html>`要素はあるレンダリングから次のレンダリングまで持続します。

Turbo Driveと直接やり取りして訪問の発生方法を制御したりリクエストのライフサイクルに接続したりすることは可能ですが、ほとんどの場合でこれはいくつかの規約を採用するだけですぐにできる互換となります。

# Turbo Frames: 複雑なページを分解する

ほとんどのWebアプリケーションはいくつかの独立したセグメントを含むページを表示します。ディスカッションページの場合、上部にナビゲーションバー、中央にメッセージのリスト、下部に新しいメッセージを追加するためのフォーム、関連トピックを含むサイドバーがあります。通常、このディスカッションページを生成するということは各セグメントをシリアル化された方法で生成し、それらをすべてつなぎ合わせてから結果を単一のHTMLレスポンスとしてブラウザーに配信することを意味します。

Turbo Framesを使用するとこれらの独立したセグメントをフレーム要素内に配置してナビゲーションのスコープを設定し、遅延読み込みを行うことができます。スコープナビゲーションとはリンクのクリックやフォームの送信などフレーム内のすべての操作がそのフレーム内で発生し、ページの残りの部分が変更またはリロードされないようにすることを意味します。

独立したセグメントを独自のナビゲーションコンテキストでラップするには`<turbo-frame>`タグで囲みます。例えば：

```erb
<turbo-frame id="new_message">
  <form action="/messages" method="post">
    ...
  </form>
</turbo-frame>
```

上記のフォームを送信すると、TurboはリダイレクトされたHTMLレスポンスから一致する`<turbo-frame id="new_message">`要素を抽出し、そのコンテンツを既存の`new_message`フレーム要素にスワップします。ページの残りの部分は元のままです。

フレームはスコープナビゲーションに加えてコンテンツの読み込みを延期することもできます。フレームの読み込みを遅らせるには、自動的に読み込まれるURLを値とする`src`属性を追加します。スコープナビゲーションと同様に、Turboは結果のレスポンスから一致するフレームを見つけて抽出し、そのコンテンツを所定の位置にスワップします。

```erb
<turbo-frame id="messages" src="/messages">
  <p>This message will be replaced by the response from /messages.</p>
</turbo-frame>
```

これは昔ながらのフレームや`<iframe>`のように聞こえるかもしれませんが、Turbo Frameは同じDOMの一部であるため、「実際の」フレームに関連する奇妙さや妥協はありません。Turbo Framesは同じJavaScriptコンテキストの一部である同じCSSによってスタイルが設定され、追加のコンテンツセキュリティの制限下に置かれることはありません。

セグメントを独立したコンテキストに変換することに加えてTurbo Framesは以下を提供します。

1. **効率的なキャッシング** 上記のディスカッションページの例では、新しい関連トピックが表示されるたびに関連トピックサイドバーの有効期限が切れる必要がありますが、中央のメッセージリストは期限切れになりません。すべてが1ページだけの場合、個々のセグメントのいずれかが期限切れになるとすぐにキャッシュ全体が期限切れになります。フレームを使用すると各セグメントが個別にキャッシュされるため、従属キーが少なく寿命が長いキャッシュが得られます。
2. **並列実行** 遅延読み込みされた各フレームは独自のHTTPリクエスト/レスポンスによって生成されます。つまり別のプロセスで処理できます。これによりプロセスを手動で管理する必要なく並列化された実行が可能になります。エンドツーエンドで完了するのに400ミリ秒かかる複雑な複合ページは、最初のリクエストが50ミリ秒しかかからず、3つの遅延読み込みフレームがそれぞれ50ミリ秒かかるフレームで分割される可能性があります。これでページ全体が100ミリ秒で完了します。これはそれぞれ50ミリ秒かかる3つのフレームが順次ではなく同時に実行されるためです。
3. **モバイル対応** モバイルアプリでは通常は大きくて複雑な複合ページを作成することはできません。各セグメントには専用の画面が必要です。Turbo Framesを使用して構築されたアプリケーションでは複合ページをセグメントに変換するこの作業は既に完了しています。これらのセグメントは変更することなくネイティブシートや画面に表示できます（すべて独立したURLを持っているため）。

# Turbo Streams: ライブページの変更を配信

非同期アクションに応答して部分的なページ変更を行うことでアプリケーションを生き生きと感じさせることができます。Turbo Framesは1つのフレーム内での直接的なやり取りに応答してこのような更新を提供しますが、Turbo StreamsはWebSocket接続、SSE、またはその他のトランスポートを介して送信された更新に応答してページの任意の部分を変更できます。(新しい電子メールが到着したときに自動的に更新される受信トレイを考えてみてください。)

Turbo Streamsは7つの基本アクション`append`, `prepend`, `replace`, `update`, `remove`, `before`, `after`を持つ`<turbo-stream>`要素を導入します。これらのアクションと操作対象の要素のIDを指定する`target`属性を使用してページの更新に必要なすべての変更をエンコードできます。複数のストリーム要素を1つのストリームメッセージに結合することもできます。挿入または置換したいHTMLを[テンプレートタグ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)に含めるだけでTurboが残りの作業を行います。

```erb
<turbo-stream action="append" target="messages">
  <template>
    <div id="message_1">My new message!</div>
  </template>
</turbo-stream>
```

このストリーム要素は新しいメッセージとともに`div`を受け取り、それを`messages`IDのコンテナに追加します。既存の要素を置き換えるのと同じくらい簡単です:

```erb
<turbo-stream action="replace" target="message_1">
  <template>
    <div id="message_1">This changes the existing message!</div>
  </template>
</turbo-stream>
```

これはRailsの世界で最初に[RJS](https://rubyonrails.org/2006/3/28/rails-1-1-rjs-active-record-respond_to-integration-tests-and-500-other-things)と呼ばれ、次に[SJR](https://signalvnoise.com/posts/3697-server-generated-javascript-responses)と呼ばれた概念の続きですが、JavaScriptを必要とせずに実現されています。利点は同じままです。

1. **サーバー側テンプレートの再利用:** ライブページの変更は初回読み込みページの作成に使用されたのと同じサーバーサイドテンプレートを使用して生成されます。
2. **HTML over the wire:** 送信するのはHTMLだけなので更新を処理するためにクライアントサイドのJavaScript（もちろんTurbo以外）は必要ありません。はい、HTMLの中身は同等のJSONよりも少し大きいかもしれませんがgzipを使用すると通常は違いはごくわずかであり、JSONをフェッチしてHTMLに変換するために必要なクライアントサイドの作業をすべて節約できます。
3. **よりシンプルな制御フロー:** メッセージがWebSocket、SSE、またはフォーム送信のレスポンスが到着したときに何が起こるかを理解することは非常に明確です。ルーティング、イベントバブリング、またはその他の間接化は必要ありません。変更するのはHTMLだけで方法を示す単一のタグでラップされています。

現在、RJSやSJRとは異なり、Turbo Streamsアクションの一部としてカスタムJavaScript関数を呼び出すことはできません。しかし、これはバグではなく機能です。あまりにも多くのJavaScriptがレスポンスとともに送信されると、これらの手法は簡単に複雑な混乱を引き起こす可能性があります。TurboはDOMを更新することだけに真っ向から焦点を当てており、Stimulusアクションとライフサイクルコールバックを使用して追加の動作を接続することを前提としています。

# Turbo Native: iOSとAndroid向けのハイブリッドアプリ

Turbo NativeはiOSとAndroid用のハイブリッドアプリの構築に最適です。サーバーでレンダリングされた既存のHTMLを使用して、アプリの機能のベースラインカバレッジをネイティブラッパーで取得できます。そうすれば節約したすべての時間を忠実度の高いネイティブコントロールの恩恵を受ける少数の画面の作成に費やすことができます。

Basecampのようなアプリケーションには何百もの画面があります。これらの画面を1つずつ書き直すことは非常に大きな作業でありメリットはほとんどありません。最高の忠実度が実際に要求されるハイタッチインタラクションにはネイティブの火力を確保することをお勧めします。たとえばBasecampの"New For You"受信トレイのようなもので、スワイプコントロールを使用して適切に感じる必要があります。しかし1つのメッセージを表示するようなほとんどのページは完全にネイティブであったとしてもそれほど良くはありません。

ハイブリッドに移行すると開発プロセスがスピードアップするだけでなく、遅くて面倒なアプリストアのリリースプロセスを経ることなく、アプリをより自由にアップグレードできるようになります。HTMLで行われたことはすべてWebアプリケーションで変更でき、すべてのユーザーがすぐに利用できるようになります。ビッグテックが変更を承認するのを待つ必要もユーザーがアップグレードするのを待つ必要もありません。

Turbo NativeはiOSとAndroidで利用可能な推奨される開発プラクティスを使用していることを前提としています。これは、ネイティブAPIを抽象化するフレームワークではなく、プラットフォーム間でネイティブコードを共有できるようにしようとするフレームワークでもありません。共有可能な部分はサーバー側でレンダリングされるHTMLです。ただし、ネイティブコントロールは推奨されるネイティブAPIで記述されています。

詳細なドキュメントについては[Turbo Native: iOS](https://github.com/hotwired/turbo-ios)および[Turbo Native: Android](https://github.com/hotwired/turbo-android)リポジトリを参照してください。[iOS](https://apps.apple.com/us/app/hey-email/id1506603805)と[Android](https://play.google.com/store/apps/details?id=com.basecamp.hey&hl=en_US&gl=US&pli=1)のHEYのネイティブアプリを見て、Turboを利用したハイブリッドアプリをどれだけうまく作成できるかを感じてください。

# バックエンドフレームワークと統合する

Turboを使用するためにバックエンドフレームワークは必要ありません。すべての機能はさらに抽象化することなく直接使用できるように構築されています。しかし、Turboと統合されたバックエンドフレームワークを使用する機会があれば、作業はずっと簡単になります。[このようなRuby on Railsの統合のリファレンス実装を作成しました。](https://github.com/hotwired/turbo-rails)