HTMX:ReactやVueを使わずに「爆速」で動的Webサイトを構築する

Development tutorial - IT technology blog
Development tutorial - IT technology blog

JavaScriptでプロジェクトを複雑にするのはやめましょう

ページをリロードせずに「いいね」ボタンを実装したり、リアルタイムの検索フィルターを作りたいだけではありませんか?それなら、ReactやVueを急いで導入する必要はありません。実際、多くの小規模プロジェクトが、わずか数行のテキストを表示するためだけに300MBものnode_modulesを抱え込んでいます。WebpackやViteの設定に、ビジネスロジックを書くよりも時間がかかってしまうことさえあります。

以前、社内ダッシュボードのためにPythonのバックエンドとReactのフロントエンド間で状態(state)を同期させるだけで、午前中を丸々潰したことがあります。ロジックが2カ所に分散してしまい、メンテナンスが非常に困難になりました。本来は非常にシンプルなタスクに対して、シングルページアプリケーション(SPA)モデルを過剰に利用してしまっているのです。

HTMXは、軽量な代替案として登場しました。Webの本来の姿に立ち返りつつ、スムーズなユーザー体験を実現してくれます。

HTMXとは?

HTMXは巨大なフレームワークではありません. わずか14KB(gzip圧縮時)ほどの超軽量ライブラリです。素のJavaScriptを書いたり外部ライブラリを使ったりする代わりに、HTML属性を通じてブラウザのモダンな機能に直接アクセスできるようになります。

通常、HTTPリクエストを送信できるのは<a>タグと<form>タグだけです。HTMXはその制限を打ち破ります。今や、divbuttoninputなど、あらゆる要素がGETPOSTPUTDELETEリクエストを送信できます。生のJSONを受け取る代わりに、サーバーはHTMLの断片(HTML fragment)を返します。HTMXは、そのコードを指定した場所に自動的に挿入します。

Hypermedia-driven Development (HDD) モデルの威力

視点を変えてみましょう。従来のReactモデルでは、サーバーはJSONデータを返すだけの「配達員」に過ぎませんでした。しかしHTMXでは、サーバーはUIを直接生成する「建築家」の役割を担います。

HDDを採用すると、すべてのビジネスロジックがバックエンドに集約されます。ブラウザはサーバーから送られてきたものを表示するだけです。このアプローチにより、複雑なJSON変換(シリアライズ/デシリアライズ)のステップが完全に不要になります。開発時間を大幅に短縮し、同期エラーを最小限に抑えることができます。

バックエンドの実装中にデータを素早く処理する必要がある場合は、toolcraft.appのツールを試してみてください。私はよく、重い拡張機能を開かずにJSONの整形や正規表現のテストを行うために利用しています。

実践:JSなしでFlaskにHTMXを統合する

シンプルなTodoリストアプリを作成します。ページ全体をリロードすることなく、新しいアイテムをスムーズに追加できるようにします。

Flask環境のインストール:

pip install Flask

app.pyのロジックを記述:

from flask import Flask, render_template, request

app = Flask(__name__)
todos = ["HTMXを学ぶ", "itfromzeroの記事を書く"]

@app.route("/")
def index():
    return render_template("index.html", todos=todos)

@app.route("/add-todo", methods=["POST"])
def add_todo():
    new_todo = request.form.get("todo")
    if new_todo:
        todos.append(new_todo)
    # 新しいアイテムのみをHTMLとして返す
    return f"<li>{new_todo}</li>"

if __name__ == "__main__":
    app.run(debug=True)

テンプレート index.html の構成:

<script src="https://unpkg.com/[email protected]"></script>

<h1>タスク一覧</h1>

<form hx-post="/add-todo" hx-target="#todo-list" hx-swap="beforeend" hx-on::after-request="this.reset()">
    <input type="text" name="todo" placeholder="新しいタスクを追加...">
    <button type="submit">追加</button>
</form>

<ul id="todo-list">
    {% for todo in todos %}
        <li>{{ todo }}</li>
    {% endfor %}
</ul>

覚えておくべき3つの重要な属性:

  • hx-post: フォーム送信時にPOSTリクエストを送信します。
  • hx-target: サーバーからの新しいコンテンツを挿入する場所を指定します。
  • hx-swap="beforeend": 全体を置き換えるのではなく、リストの最後に新しいコンテンツを挿入します。

パフォーマンス最適化のためのFastAPIとHTMXの組み合わせ

非同期処理に優れたFastAPIは、HTMXにとって完璧な相棒です。この組み合わせにより、クライアントサイドのレンダリングを待つよりも、レイテンシ(遅延)を大幅に削減できます。

from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/", response_class=HTMLResponse)
async def read_item(request: Request):
    return templates.TemplateResponse("index.html", {"request": request, "items": ["FastAPI", "HTMX"]})

@app.post("/search", response_class=HTMLResponse)
async def search(request: Request, q: str = Form(...)):
    results = [item for item in ["Python", "JavaScript", "C++"] if q.lower() in item.lower()]
    return templates.TemplateResponse("partials/results.html", {"request": request, "results": results})

ここでのコツは「部分(partials)」テンプレートを使用することです。HTMXからのリクエストがあった場合にのみ、UIの特定の部分だけをレンダリングします。この手法により、帯域幅を節約し、サーバーのCPU負荷を軽減できます。

実体験から得た重要な注意点

いくつかの管理システムにHTMXを導入した後、4つの教訓を得ました:

  1. 適切なツールを選ぶ: HTMXはGoogleマップやオンライン版Photoshopのようなアプリを作るためのものではありません。ブログ、ダッシュボード、ECサイトなどで真価を発揮します。
  2. hx-triggerを活用する: 属性一つでライブ検索機能を実装できます。サーバーへのリクエスト過多を防ぐために、keyup changed delay:500msを使用しましょう。
  3. データの安全性: XSS攻撃を防ぐために、常にデータをエスケープしてください。Jinja2などのテンプレートエンジンは通常これを自動で行いますが、念のため確認することをお勧めします。
  4. エラーのデバッグ: ブラウザのDevToolsでNetworkタブを開いて監視してください。HTMXのリクエストは非常に明快で、HTMLの内容が直接返ってくるのが確認できます。

Lời kết

HTMXはJavaScriptフレームワークを完全に置き換えるものではありません。しかし、バックエンド開発者が目まぐるしく変わるJSのエコシステムに翻弄されることなく、自信を持ってインタラクティブなアプリケーションを構築する助けになります。個人プロジェクトや社内ツールを作っているなら、ぜひHTMXを試してみてください。時には、シンプルさこそが効率化の鍵となります。

Share: