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

Railsの技: Turbo Frameとskeleton loaderでHotwireのコンテンツをlazy loadingする(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

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

Railsの技: Turbo Frameとskeleton loaderでHotwireのコンテンツをlazy loadingする(翻訳)

HotwireはBasecampが提供する新しいフロントエンドツール集で、最小限のJavaScriptを書くだけで「リアクティブな」Railsアプリを構築できます。

Hotwireの機能の中で、サーバーサイドでレンダリングしたHTMLをリアルタイムでストリーミングできる機能が最大の目玉だと考える人もいますが、私の推しはTurbo Frameが追加されたことです。

Turbo Frameはiframeの超高性能版で、使ってみると実に感心します。Frameはページの一部を表現し、そこに独自のナビゲーションコンテキストを内包しています。

中でもひときわ強力なのは、Turbo Frameのlazy-loading(遅延読み込み)機能です。このパターンの例としては、普段よく目にするGitHubのactivityフィードがあります。

GitHub Activity Feed: Lazy load

最初にページの「外殻」を読み込み、続いてAJAX呼び出しを実行すると、コンテンツの残りをフェッチしてページを埋められます。これは遅いページの高速化に絶大な効果を発揮します。

しかし、この機能を使うとページコンテンツががたついてしまうという欠点があります。「読み込み中」のスピナー表示は小さい四角形ですが、表示されると多数のイベントがフィードされて四角形が下に展開されます。

これを解決する方法のひとつは、"skeleton screen"か"skeleton loader"を使うことです。このUIパターンでは、空白のコンテンツをプレースホルダとして用いることで、コンテンツが最終的に読み込まれたときのがたつきを軽減します。

この2つの概念は、ピーナッツバターとゼリーのように相性抜群です。

使い方

Turbo Frameの基本的なlazy-loadingは以下のようになります。

<turbo-frame id="feed" src="/feed">
 /feedが読み込まれるとこのコンテンツが置き換えられる
</turbo-frame>

ここにsrc属性を指定すると、Frameはページ読み込み時に自動的にAJAXリクエストを発行し、レスポンス内で<turbo-frame>にマッチする部分をそのコンテンツで置き換えます。

さらに、loadingプロパティに"eager"または"lazy"も設定できます。"eager"は直ちに読み込みを開始し、"lazy"はフレームがページに出現したときに読み込みを開始します。

GitHub ActivityのフィードをRailsで行うと、以下のような感じになるでしょう。

<!-- app/views/home.html.erb -->

<div>(他のコンテンツ)</div>

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
  読み込み中...
<% end %>

この基本的な「読み込み中...」メッセージを独自のskeleton loaderに置き換えることでページをレベルアップできます。Tailwind CSSに組み込まれているanimate-pulseクラスのおかげで実に簡単です。

適当な灰色の四角形をフレームの初期コンテンツとして追加します。

<!-- app/views/home.html.erb -->

<div>(他のコンテンツ)</div>

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
  <div class="flex flex-col space-y-6">
    <% 10.times do %>
      <div class="animate-pulse flex space-x-4">
        <!-- アバター -->
        <div class="rounded-full bg-gray-400 h-12 w-12"></div>

        <!-- 詳細 -->
        <div class="flex-1 space-y-4 py-1">
          <div class="h-4 bg-gray-400 rounded w-3/4"></div>
          <div class="space-y-2">
            <div class="h-4 bg-gray-400 rounded"></div>
            <div class="h-4 bg-gray-400 rounded w-5/6"></div>
          </div>
        </div>
      </div>
    <% end %>
  </div>
<% end %>

仕上げに、activity_feed_pathアクションが返すコンテンツをTurbo Frameにマッチするようラップしておきます。これによりフレームのコンテンツが自動的に差し替えられ、読み込み中のステートが更新されます。

class ActivityFeedController < ApplicationControler
  def show
    @events = Current.user.activity.last(20)
  end
end

注意: このレスポンスではFrameにsrc属性やloading属性を設定しないでください(設定すると無限ループになってしまいます)。

<!-- app/views/activity_feed/show.html.erb -->

<%= turbo_frame_tag :feed do %>
  <%= render partial: "feed_item", collection: @events %>
<% end %>

Turbo Frameの真の力を理解できれば、昔ながらのHTMLページをlazy-loadingできるようになり、アプリのあらゆる場所で役に立つでしょう。

参考情報

関連記事

HotwireはRailsを「ゼロJavaScript」でリアクティブにできるか?前編(翻訳)


CONTACT

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