Top
beta

About

Hotwire for Frontend Developers

フロントエンドエンジニアのためのHotwire入門

「Hotwire for Frontend Developers(フロントエンドエンジニアのためのHotwire入門)」は、Ruby on Railエンジニアに限らず、すべてのウェブ開発者にHotwireの良さを体感していただくために作成したサイトです。

本サイトはHotwireで書かれたページとReact (Next.js)で書かれた実際のウェブページを横に並べ、それぞれのUXを体験していただき、さらにGitHubに上げたコードを確認していただきながら、実装の理解を深けめていただく構成になっています。

Hotwireは優れたUXを小さいチームで作るための技術#

Hotwireおよびその前身の技術(TurboLinks, UJS)は、ウェブデザイン会社として創業した37signals社が、BasecampHeyなどのSaaS製品を開発するために作成されたものです。ユーザ向けのすべての画面で使われているのはもちろんのこと、iOS、Android用のモバイルアプリでもHotwireは使われています。このように、Hotwireが人気の有料SaaSアプリでも十分なUI/UXを提供できることが証明されています。

一方で37signals社の各チームのサイズはデザイナーを含めてたったの3人です。開発効率が非常に高く、小さいチームでの開発ができることも実証されています。

Next.jsに勝るとも劣らないUI/UX #

「Hotwireは実装が楽だけど、限界がある」と考えている人がいます。React/Next.jsほどのUI/UXは作れないと思っている人がいます。「管理画面はHotwireでも良いけど、お客様向け画面はReactにしよう」という声も聞こえます。しかしこれは誤りです。お客様がお金を払ってでも使いたくなる製品がHotwireだけで作成できることを、37signals社が明確に実証ずみです。さらに本サイトでは細かい仕組みの議論をしながら、HotwireのUI/UXの方がむしろ優れている面もあることを紹介します。

もちろん全てにおいてHotwireがReact/Next.jsに勝るわけではありません。ただし少なくとも対等であると考えて間違いありません。

Hotwire is capable of High fidelity UI/UX

Hotwireの構成 #

Hotwireは以下の3つのパーツから構成されています。

  • Turbo: Turboはサーバにレクエストを投げて、返ってきたHTMLをDOMに埋め込むためのライブラリです。いわゆる非同期通信(AJAX)を担当します。大きな特徴はJSONを介して通信をせず、敢えてHTMLのみを使う点です。HTMLのみの通信により、ブラウザ側の処理を大幅に簡略化しています。Turbo自身はさらにTurbo Drive, Turbo Frames, Turbo Streamsに分かれています。
    (Turbo以前のRailsではサーバからJavaScriptを返すことが一般的で、Turboよりむしろ遥かに柔軟性がありました。この柔軟さを捨てて、シンプルさを優先したのがHotwireと言えます)
  • Stimulus: StimulusはHTMLを新たにレンダリングするのではなく、すでにあるHTMLにJavaScriptを結びつけることに注力しています。再利用可能なControllerというまとまりを作ることで、イベントハンドラのスパゲッティを避けています
  • Strada: StradaはWebとiOS, Androidを繋ぎ合わせる役割を担う、モバイルアプリ作成のためのライブラリです

上記の3つを組み合わせることにより、シンプルさを維持しつつ、モダンフロントエンドの要件を十分に満たすウェブサイト、さらにはネイティブアプリの作成が可能になります。

大雑把に言えば、守備範囲としてはHotwire == React + Next.js + React Nativeの関係となります。

hotwire component structure image

Turboの構成 #

Turboはfetchを使い、ウェブページの部分置換をするライブラリです。Hotwireの中心的な技術です。置換する範囲と付随する機能に応じて、3種類の置換方法があります。

  • Turbo Drive:Turbo Drivebodyタグの中身を丸々置換する技術です。Next.jsのrouter、あるいはReact Routerに相当し、ページ全体をSPA的に置換・遷移する仕組みです。Linkタグのような特別なものは使わず、aタグが全て自動的にTurbo Driveを使うようになります。
  • Turbo Frames: Turbo Framesは部分置換を実現するものです。画面の一部を置換するだけではなく、画面を 「枠」 に分割する性質があり、デフォルトでは枠内の a タグおよび form タグも中に閉じ込めるように動きます。ルータとの連携Lazy Loadなども用意され、画面の部分置換だけでなく、関連するUXもパッケージされています。柔軟性も高く、これだけでほとんどの部分置換は可能です (Turbo Streamsをあまり使わなくても用が足りてしまう)。
    Next.js app routerのLayoutに近い性質もありますが、URLとの連動が必須ではないことや、1つのページに複数のTurbo Frameが配置できることなど、より小回りが効く柔軟な仕組みです。
  • Turbo Streams: Turbo Streamsは画面を細かく、柔軟に置換する技術です。IDで指定された要素を1つずつ追加・置換したり、削除したりできます。またWebSocketを介した置換も可能になっています。柔軟性は非常に高いのですが、それだけに置換ステップ数が増大する傾向があり、必要なところで慎重に使うのがポイントではないかと思います。jQueryprepend(), append(), remove(), html()をHTML属性から呼び出す感覚に近いとも言えます。Turbo StreamsとTurbo Framesの使い分けは明確ではなく、人よってどちらを多く使うかが異なります。私はTurbo Framesを多く使いますので、本サイトもこちらが中心になっています。

この他、TurboにはMorphingがあります。これはReactの差分検出処理と似たものであり、ブラウザのDOMとサーバから送られてきたHTMLの差分を検出し、なるべくブラウザのステートを保持しつつ更新処理をかけるものです。

Turboは上記のたった3つのパーツしかないのですが、実際にやってみるとこれだけでほとんどのインタラクティブなUIが作れてしまいます。

Hotwireの特徴 #

  • バックエンド技術非依存: Ruby on Railsに限らず、DjangoLaravelJavaNodeWordpressなど、バックエンド技術がなんであってもHotwireは使用できます。実際、本サイトのHotwireデモはすべてNext.js のAPI routesで動いています。
  • 大幅なUXを向上: ウェブサイトのUXを大幅に向上させます。体感レスポンスタイムの大幅短縮、画面の部分置換など、モダンフロントエンドのUX要件をカバーできます。 具体的には本サイトの各例をお確かめください。
  • 学習時間と工数の削減: 昔ながらのサーバでのHTML生成をするアプローチです。JSON APIも使いません。ReactやNext.jsのような複雑さがありませんので、学習時間と作業工数を大幅に削減できます。
  • コンポーネント化: Hotwireのコンポーネント化は各バックエンド技術のテンプレートエンジンによります。Ruby on Railsであればpartialview helper、もしくは最近話題のViewComponentPhlexなどのコンポーネント化技術があります。Laravel, Djangoなどもそれぞれのコンポーネント化技術があります。
  • 注目されている技術です: Elixir PhoenixのLiveview、PHP LaravelのLivewire、さらにHotwire同様にバックエンド非依存のHTMXなど、Hotwireと同様のアプローチでモダンフロントエンドを作る技術が近年、注目を集めています。HTMXはDjangoで話題になっており、さらにSSG/SSR JavaScriptフレームワークのAstroではHTMXを意識したpage partialsという機能も導入されています。
  • セキュリティが高い: レンダリング済みのHTMLのみをブラウザに送信するので、誤ってプライベートな情報を漏洩する心配がありません。例えば秘密キーをブラウザに預ける必要がなく、またJSON APIに機密情報を流してしまうこともありません。詳しくはページ遷移の解説で議論しています。

本サイトの構成 #

  • 本サイトのコードはすべてGitHubに公開しています。またVercelでデプロイしています。コードをGitHubで確認しながら、UXをデプロイ先サイトで確認すると理解が深まるのではないかと思います。
  • HotwireはEJS: Hotwireはバックエンド技術非依存なので、HTMLが出力できればどこでも動きます。本プロジェクトでは Next.js pages routerAPI routesから HTMLをレスポンスとして返しています (/api/hotwire/配下)。 テンプレートエンジンはEJSを使っています。Hotwireの構成要素であるTurboStimulusはそれぞれbuild済みのものをダウンロードし、public/hotwire/javascriptに配置しています。 またCSSはTailwindを使用しています。
  • ReactはNext.js pages router: Hotwireと比較するためのReact側は、目まぐるしく変わるフロントエンド開発環境の中でも、比較的頻繁に見られる技術を選定しました。
    • フレームワークはNext.jsを使用しています。ただしまたapp routerはまだ使用しているプロジェクトが少ないと考え、pages routerを中心に作成しています。ただし一部app routerと比較したいケースの時はこれも使っています。
    • 内容が頻繁に更新されるウェブアプリ(例えば業務アプリや管理画面、注文予約アプリなど)を想定しているため、古いデータが表示されたままになってしまうタイプのキャッシュは使用していません。特にapp routerを使っている時はRoute CacheFull Route CacheData Cacheオフにしています
  • 遅延: 高速なサイトだとどんなフロントエンド技術を使ってもサクサク動いてしまいます。何をやっても非常に快適になってしまい、技術の違いが見えなくなります。そこで本サイトではあえて数百msの遅延を入れています(例示しない内容に応じて増やしています)。ただし静的なファイルやNext.jsのSSGなど、一般にCDNの載せるようなものについては遅延を入れていません。

Hotwireのリソース #