HN 日本語サマリー

← 一覧へ戻る
Web開発

WebSockets経由のHTML:ほとんどJavaScriptなしでリアルタイムSPAを構築する

HTML over WebSockets: real-time SPAs with barely any JavaScript (en.andros.dev)

231 pointsby redbell167 コメント

要約

WebSockets経由のHTMLは、サーバーサイドでHTMLをレンダリングし、それをクライアントに送信することで、ほとんどJavaScriptを使用せずにシングルページアプリケーション(SPA)を構築するアプローチです。このパターンは、レンダリングロジックをバックエンドに集約し、APIや複雑なフロントエンドフレームワークの必要性を排除することで、開発の簡素化とパフォーマンスの向上をもたらします。

全文翻訳

SPA(シングルページアプリケーション)の構築は複雑なパズルです。ビューを描画するJavaScriptフレームワーク、JSONを提供するAPI、そして契約を通じて互いを理解することを強制される2つの独立したコードベース。これは受け入れられた、専門化されたシナリオです。しかし、標準であるからといってそれが唯一の方法であるわけではありません。ここでは、新しいものではありませんが、長年注目を集めている別のアプローチ、WebSockets経由のHTMLを紹介します。 アイデアはこうです:JSONを送信してブラウザでHTMLを組み立てる代わりに、サーバーは既に構築されたHTMLを送信し、クライアントはそれを適切な場所に配置するだけです。すべてのレンダリングロジックは、単一の言語で、契約やAPIなしで、バックエンドに残ります。このパターンはハイパーメディアまたはワイヤー経由のHTMLとして知られています。 HTMLがどのように移動するかは、レイテンシと通信の双方向性を決定します。3つのバリエーションがあります:HTTP経由、リクエストごと、htmxやUnicornのようなもの。SSE経由、サーバーからクライアントへの片方向の連続チャネルを開く、Datastarのようなもの。WebSockets経由、永続的で双方向のチャネル、Phoenix LiveViewやDjango LiveViewのようなもの。 チャネルはアプリケーションのアーキテクチャとその通信パターンを決定するほど重要です。この記事では、WebSockets経由のHTMLについて説明します。このファミリーのリアルタイムで双方向のバリエーションです。単一の言語で、契約なしで、単一のレンダリングエンジンで、ほとんどJavaScriptなしでSPAを構築できます。それが何であるか、どのように機能するか、そしてHTTPやSSEのいとこたちと比較していつ価値があるかを見ていきます。 起源 Phoenix(Elixirエコシステムで最も人気のあるフレームワーク)の作成者であるChris McCordは、ElixirConf 2019でLiveViewと呼ばれるテクノロジーを発表しました。わずか15分で、レンダリングJavaScriptやビューを管理する人気フレームワーク(React、Angular、Vueなど)を追加することなくリアルタイムで動作するTwitterクローンを構築し、バックエンドにとどまりながら優れたパフォーマンスで生産的になれることを証明しました。それ以来、このソリューションは人気を博し、他の開発者に他の言語でWebSockets経由のHTML実装を構築するよう促しています。 フロントエンドの良い部分を諦めることなく、バックエンドに戻ることができます。 仕組みは? 一見そう見えないかもしれませんが、クライアントではJavaScriptが使用されています。その仕事はレンダリングではなく、WebSocketsとの通信チャネルを作成し、受信したHTMLを適切な場所に配置することです。さらに、アニメーション、イベント処理などの二次的なタスクもあります。 McCordのソリューションは、フロントエンドにJSONを送信するのではなく、前処理を必要としないHTMLを送信することです。そのようにして、レンダリングの負荷とすべてのロジックをバックエンドに移行します。 OK、でも...サーバーがリクエストなしで新しいコンテンツをすぐに送信するにはどうすればよいですか?簡単です:WebSocketsで。 導入部の従来のシステムをレビューしましょう。WebからHTTPリクエストを送信すると、ブラウザがアクションを開始し、すべての生のデータを含むJSONが応答として返されます。次のステップは、それを解釈して対応するHTMLを構築することです。 sequenceDiagram participant C as Browser participant S as Server C->>S: 1. HTTPリクエスト (GET /api/article/2/) および認証 S->>S: 2. DBクエリ S->>S: 3. 記事データを含むJSONを構築 S-->>C: 4. JSONを返す C->>C: 5. JSONを解析 C->>C: 6. レンダリングエンジンでHTMLを構築 WebSockets経由のHTMLでは、同じリクエストが永続的なチャネルを介して送信され、応答は既に組み立てられたHTMLであり、間にJSONはありません。そして、チャネルは決して閉じられないため、サーバーは先回りしてクライアントが要求する前に変更を送信することさえできます。 WebSocketsを使用したフローは次のようになります。初期接続と認証は無視します。これらはチャネルが開かれたときにのみ発生します。 sequenceDiagram participant C as Browser participant S as Server (Back-End) C->>S: 1. テキストを送信:「/article/2/が欲しい」 S->>S: 2. DBクエリ S->>S: 3. テンプレートエンジンでHTMLをレンダリング S-->>C: 4. HTML/CSS/JS"..."を組み立てて返す C->>C: 5. HTMLを適切な場所に配置 シンプルでエレガントで高速です。 クライアントはHTMLを適切な場所に配置し、イベントをリッスンする責任を負います。サーバーが残りを処理します。 すべてがバックエンドにあるため、クライアントの状態やレンダリングロジックについて心配する必要はありません。 完全な複雑なサイクル(接続の開始と認証を含む)は次のようになります。 sequenceDiagram participant C as Browser participant S as Server (Back-End) C->>S: 1. WebSocket接続を開き、認証する note over C,S: 単一の永続的なチャネル C->>S: 2. テキストを送信:「/article/2/が欲しい」 S->>S: 3. DBクエリ S->>S: 4. テンプレートエンジンでHTMLをレンダリング S-->>C: 5. HTML/CSS/JS"..."を組み立てて返す C->>C: 6. HTMLを適切な場所に配置 note over S,C: サーバーはクライアントに要求されずに変更をプッシュすることもできます(ブロードキャスト) さらに、そのアーキテクチャにより、他のソリューションよりも固有の利点があります。 利点は何ですか? レンダリングエンジンは1つだけで、複雑さが軽減されます。 APIを構築する必要はありません:サーバーはHTMLを生成し、クライアントに送信します。仲介者はいません。 状態はサーバーにあります。ステートレスなリクエスト-レスポンスではありません:接続されたクライアントごとに、その場所を記憶しているプロセスがあります。htmxとは対照的です。htmxは意図的にステートレスです。 JSONやGraphQLの仲介者なしで、データベースに直接接続できます。 真のリアルタイム:クライアントはポーリングなしで、可能な限り速く変更を受け取ります。 ブロードキャスト:サーバーは一度にすべての接続されたクライアントに変更をプッシュできます。チャット、ダッシュボード、マルチプレイヤーゲームの構築が無料でできます。 アクションごとのトラフィックとレイテンシの削減:単一の永続的な接続により、TCPハンドシェイクとHTTPヘッダーの繰り返しが回避されます。これは「WebSocketプロトコルが魔法のように速い」ということではありません(HTTP/2とHTTP/3はそのギャップをリクエスト-レスポンスで大きく縮めました)。これは、ラウンドトリップをスキップして組み立てられたHTMLを送信することです。 React、Angular、Vueのような重いフレームワークなしで、ほとんどJavaScriptなしでSPAを構築できます。 妥当なSEO:HTMLはサーバーでレンダリングされるため、最初のロードはインデックス化可能です。ただし、クローラーはWebSocketで後から到着する更新を表示しないため、重要なコンテンツはその最初の応答に含まれている必要があります。 インジェクションに対する安全性向上:サーバーはチャネル経由で送信する前にHTMLをレンダリングしてエスケープするため、<script>を忍び込ませようとする試みは不活性なテキストとして移動し、コードではなくプレーンな文字として隣人の画面に到達します。チャットを簡単にするのと同じアーキテクチャが、XSSに対して免疫を与えます。 欠点は何ですか? サーバーはより多くのリソースを必要とします:WebSocketを開いたままにし、通常は各クライアントの状態をメモリに保持します。水平スケーリングは、その状態を共有することを強制します(Djangoでは、Channels + ASGIサーバー + Redisをチャネルレイヤーとして使用)。ただし、問題は多数の同時クライアントでしか発生せず、慎重な設計で軽減できます。私のサイトは、他のサービスが並行して実行されているRaspberry Pi 3と同等のハードウェアで、600人の同時読者からのピークを問題なく処理しました。 レイテンシ:物理的なレイテンシが大きい場合、「瞬時」という感覚は損なわれます。 オフラインでは機能しません。接続が切断されると、サイトは機能しなくなります。再接続エクスペリエンスとフォールトトレランスを設計する必要があります。 最初の学習曲線は、<script>をドロップインするよりも急です:WebSocketサーバーを実行することは簡単ではなく、LiveViewパターンを処理することを学ぶ必要があります。 現在の状況:どのフレームワークが存在しますか? ハイパーメディア運動は、ほぼすべての言語で実装があります。トランスポート列を見てください:WebSocket(LiveViewパターン、リアルタイムおよび双方向)で実行されるものは、その双方向チャネルが必要ない場合に、HTTPおよびSSEのいとこと共存します。 ここから始めることができます: 言語 フレームワーク トランスポート サーバープッシュ? ステータス Elixir Phoenix LiveView WebSocket はい 成熟(1.x、LiveView 1.0は2024年12月) Ruby Hot