Turbo 3: Turbo Framesで分解

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

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

Turbo Framesを使用すると、ページの事前定義された部分を要求に応じて更新できます。フレーム内のすべてのリンクとフォームがキャプチャされ、レスポンスの受信後にフレームの内容が自動的に更新されます。サーバーが完全なドキュメントを提供するか、要求されたフレームの更新されたバージョンを含むフラグメントのみを提供するかに関係なく、その特定のフレームのみがレスポンスから抽出され、既存のコンテンツが置き換えられます。

フレームは`<turbo-frame>`要素でページのセグメントをラップすることによって作成されます。各要素には一意のIDが必要で、これはサーバーから新しいページをリクエストするときに置き換えられるコンテンツと一致させるために使用されます。1つのページに複数のフレームを含めることができ、それぞれが独自のコンテキストを確立します。

```html
<body>
  <div id="navigation">Links targeting the entire page</div>

  <turbo-frame id="message_1">
    <h1>My message title</h1>
    <p>My message content</p>
    <a href="/messages/1/edit">Edit this message</a>
  </turbo-frame>

  <turbo-frame id="comments">
    <div id="comment_1">One comment</div>
    <div id="comment_2">Two comments</div>

    <form action="/messages/comments">...</form>
  </turbo-frame>
</body>
```

このページには2つのフレームがあります。1つはメッセージ自体を表示するフレームでメッセージを編集するためのリンクがあります。もう1つはすべてのコメントを一覧表示するものと別のコメントを追加するフォームがあります。それぞれがナビゲーション用の独自のコンテキストを作成し、リンクとフォームの送信の両方をキャプチャします。

メッセージを編集するためのリンクがクリックされると、`/messages/1/edit`によって提供されるレスポンスでその`<turbo-frame id="message_1">`セグメントが抽出され、クリック元のフレームがコンテンツに置き換えられます。編集されたレスポンスは次のようになります。

```html
<body>
  <h1>Editing message</h1>

  <turbo-frame id="message_1">
    <form action="/messages/1">
      <input name="message[name]" type="text" value="My message title">
      <textarea name="message[content]">My message content</textarea>
      <input type="submit">
    </form>
  </turbo-frame>
</body>
```

`<h1>`が`<turbo-frame>`の中にないことに注意してください。これは編集時にフォームがメッセージの表示を置き換えるときに無視されることを意味します。フレームが更新されると`<turbo-frame>`に一致する内容のみが使用されます。

したがって、ページは2つの目的を簡単に果たすことができます。フレーム内の所定の場所で編集を行うか、ページ全体がアクション専用のフレームの外側で編集を行います。

# Eager-Loadingフレーム

フレームを含むページが読み込まれるときに、フレームを設定する必要はありません。`turbo-frame`タグに`src`属性が存在する場合、タグがページに表示されるとすぐに参照されたURLが自動的に読み込まれます。

```html
<body>
  <h1>Imbox</h1>

  <div id="emails">
    ...
  </div>

  <turbo-frame id="set_aside_tray" src="/emails/set_aside">
  </turbo-frame>

  <turbo-frame id="reply_later_tray" src="/emails/reply_later">
  </turbo-frame>
</body>
```

このページはページをロードするとすぐに受信ボックスで利用可能なすべての電子メールを一覧表示しますが、その後、保留された電子メールまたは後で返信を待っている電子メールのためにページの下部に小さなトレイを表示するように 2つの後続のリクエストをします。これらのトレイは`src`で参照されているURLに対して作成された個別のHTTPリクエストから作成されます。

上記の例ではトレイは空で開始されますが、eager-loadingフレームに初期コンテンツを入力することもできます。これはコンテンツが`src`からフェッチされるときに上書きされます。

```html
<turbo-frame id="set_aside_tray" src="/emails/set_aside">
  <img src="/icons/spinner.gif">
</turbo-frame>
```

受信ボックスのページをロードすると、保留トレイが`/emails/set_aside`からロードされ、レスポンスには元の例のように対応する`<turbo-frame id="set_aside_tray">`要素が含まれている必要があります。

```html
<body>
  <h1>Set Aside Emails</h1>

  <p>These are emails you've set aside</p>

  <turbo-frame id="set_aside_tray">
    <div id="emails">
      <div id="email_1">
        <a href="/emails/1">My important email</a>
      </div>
    </div>
  </turbo-frame>
</body>
```

このページは最小化されたフォームである個々の電子メールのみが受信ボックスページのトレイフレームに読み込まれる`div`とヘッダーと説明が提供される直接の宛先として機能するようになりました。編集メッセージフォームの例と同様です。

`/emails/set_aside`の`<turbo-frame>`には`src`属性が含まれていないことに注意してください。その属性はコンテンツを提供するレンダリングされたフレームではなく、コンテンツを遅延ロードする必要があるフレームにのみ追加されます。

ナビゲーション中に新しいコンテンツを取得するとき、フレームは`<turbo-frame>`要素に`[aria-busy="true"]`を設定します。ナビゲーションが完了すると、フレームは`[aria-busy]`属性を削除します。`<turbo-frame>`で`<form>`の送信をナビゲートするとき、Turboはフレームと連携して`[aria-busy="true"]`属性を切り替えます。

ナビゲーションが終了すると、フレームは`<turbo-frame>`要素に`[complete]`属性を設定します。

# フレームの遅延読み込み

ページが最初にロードされたときに表示されないフレームは、表示されるまでロードを開始しないように`loading="lazy"`でマークできます。これは`img`の`lazy=true`属性とまったく同じように機能します。`概要`と`詳細`のペアまたはモーダル内にあるフレームのロードを遅らせるのに最適な方法です。

# フレームのロードに対するキャッシュの利点

ページセグメントをフレームにするとページの実装が簡単になりますが、これを行う同様に重要な理由はキャッシュダイナミクスを改善することです。多くのセグメントを持つ複雑なページは、特に多くの人が共有するコンテンツと個々のユーザーに特化したコンテンツが混在している場合、効率的にキャッシュするのが困難です。セグメントが多いほど、キャッシュの参照に必要な依存キーが多いほど、キャッシュのチャーンが頻繁に発生します。

フレームはさまざまなタイムスケールで変化するセグメントを分離し、さまざまな視聴者に提供するのに理想的です。ページの残りの大部分がすべてのユーザー間で簡単に共有される場合、ページのユーザーごとの要素をフレームに変換することが理にかなっている場合があります。それ以外の場合は反対のことを行うのが理にかなっています。つまり高度にパーソナライズされたページでは1つの共有セグメントがフレームに変換され、共有キャッシュから提供されます。

読み込み中のフレームをフェッチするオーバーヘッドは一般的に非常に低いですが、特にこれらのフレームがページ上で読み込みジッターを引き起こす場合は読み込む数を慎重に検討する必要があります。ただし、ページの読み込み時にコンテンツがすぐに表示されない場合、フレームは基本的に無料です。それらがモーダルの後ろに隠されているか、折りたたみの下にあるためです。

# フレーム内外へのナビゲーションのターゲティング

デフォルトではフレーム内のナビゲーションはそのフレームのみをターゲットにします。これは次のリンクと送信フォームの両方に当てはまります。ただし、ナビゲーションはターゲットを`_top`に設定することで囲んでいるフレームではなくページ全体を操作できます。またはターゲットをそのフレームのIDに設定することにより、別の名前付きフレームを駆動できます。

固定トレイの例ではトレイ内のリンクが個々のメールを指しています。これらのリンクで`set_aside_tray`のIDに一致するフレーム タグを検索しないでください。そのメールに直接移動します。これは`target`属性でトレイフレームをマークすることによって行われます。

```html
<body>
  <h1>Imbox</h1>
  ...
  <turbo-frame id="set_aside_tray" src="/emails/set_aside" target="_top">
  </turbo-frame>
</body>

<body>
  <h1>Set Aside Emails</h1>
  ...
  <turbo-frame id="set_aside_tray" target="_top">
    ...
  </turbo-frame>
</body>
```

ほとんどのリンクをフレーム コンテキスト内で動作させたいが、他のリンクは動作させたくない場合があります。これはフォームにも当てはまります。`data-turbo-frame`フレーム以外の要素に属性を追加して、これを制御できます。

```html
<body>
  <turbo-frame id="message_1">
    ...
    <a href="/messages/1/edit">
      Edit this message (within the current frame)
    </a>

    <a href="/messages/1/permission" data-turbo-frame="_top">
      Change permissions (replace the whole page)
    </a>
  </turbo-frame>

  <form action="/messages/1/delete" data-turbo-frame="message_1">
    <a href="/messages/1/warning" data-turbo-frame="_self">
      Load warning within current frame
    </a>

    <input type="submit" value="Delete this message">
    (with a confirmation shown in a specific frame)
  </form>
</body>
```

# フレームナビゲーションをページアクセスに昇格させる

フレームのナビゲートは、ドキュメントの残りの状態 (現在のスクロール位置やフォーカスされた要素など) を保持しながら、ページのコンテンツの一部を変更する機会をアプリケーションに提供します。フレームへの変更をブラウザの[履歴](https://developer.mozilla.org/en-US/docs/Web/API/History)にも影響させたい場合があります。

フレームナビゲーションを訪問にプロモートするには`[data-turbo-action]`属性を使用して要素をレンダリングします。この属性はすべての訪問の値をサポートし、以下で宣言できます。

- `<turbo-frame>`要素
- `<turbo-frame>`をナビゲートするすべての`<a>`要素
- `<turbo-frame>`をナビゲートするすべての`<form>`要素
- `<turbo-frame>`をナビゲートする`<form>`要素内に含まれる`<input type="submit">`または`<button>`要素

たとえば、記事のページ分割されたリストをレンダリングし、ナビゲーションを「高度な」アクションに変換するフレームを考えてみましょう:

```html
<turbo-frame data-turbo-action="advance">
  <a href="/articles?page=2" rel="next">Next page</a>
</turbo-frame>
```

`<a rel="next">`要素をクリックすると、`<turbo-frame>`要素の`[src]`属性とブラウザのパスの両方が`/articles?page=2`に設定されます。

注:ブラウザーを更新した後にページをレンダリングする場合、URLパスと検索パラメーターから派生したその他の状態と共に、記事の2ページ目をレンダリングするのはアプリケーションの役割です。

# 偽造防止サポート (CSRF)

Turboは`csrf-param`または`csrf-token`のいずれかの`name`の値を持つ`<meta>`タグの存在についてDOMをチェックすることにより、 [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery)保護を提供します。例えば：

```html
<meta name="csrf-token" content="[your-token]">
```

フォームの送信時にトークンはリクエストのヘッダーに`X-CSRF-TOKEN`として自動的に追加されます。`data-turbo="false"`で作成されたリクエストはヘッダーへのトークンの追加をスキップします。