htmxで browser history API を活用する方法と a-blog cms 実装のポイント

htmx は標準で browser history API をサポートしており、属性に hx-push-url="true" を追加することで、これを簡単に利用できます。a-blog cms をバックエンドで使用する際の注意点をいくつか紹介します。

※(例)と書かれている部分は実際には動作しません。

hx-push-url 属性を設定して正しく動作する GET

記事詳細(例)

<a>タグでの GETリクエストに対しては、htmx のドキュメントに従い、hx-get属性と hx-push-url属性を追加することを推奨します。

<a href="entry-1.html" hx-get="entry-1.html" hx-push-url="true">記事詳細</a>

hx-push-url 属性を設定しても正しく動作しない POST

(例)
この部分に結果が表示されます

POSTリクエストのケースでは、例えば a-blog cms でカスタムフィールド station を選択するための <select> を含むフォームを使用し、結果を表示するテンプレートとして include/htmx/result.html を指定する方法があります。

<form hx-post="" hx-push-url="true" hx-trigger="submit" hx-target="#search-result" hx-ext="ajax-header">
 <select name="station"> 
  <option value="Tokyo">Tokyo</option>
  <option value="Osaka">Osaka</option>
  <option value="Nagoya">Nagoya</option>
 </select>
 <input type="hidden" name="field[]" value="station">
  <input type="hidden" name="tpl" value="include/htmx/result.html">
<input type="submit" name="ACMS_POST_2GET" value="検索" >
</form>
<div id="search-result">この部分に結果が表示されます</div>

a-blog cms の仕様として、<form> の POST 時 name="ACMS_POST_2GET" が送られてくると、検索条件( station = Nagoya )から URL を組み立て、その後リダイレクトされ GET に変換され、以下のような URL でアクセスした結果を取得できます。

https://example.com/htmx/result.html/field/station/Nagoya/

ここで問題が発生します。 上記の URL がブラウザに表示され履歴にも登録されますが、この URL にアクセスすると Ajax で追加される部分的な HTML になってしまいます。この検索結果で履歴に登録されて欲しい URLは、テンプレートファイル include/htmx/result.html のパスが無い以下の URL であって欲しいのです。

https://example.com/field/station/Nagoya/

hx-push-url 属性を設定しても正しく動作する POST 解決編

ここまでで a-blog cms POSTリクエスト後に URL を組み立てる ACMS_POST_2GET の処理が、htmxhx-push-url と相性が悪い状況を解決する方法を考えてみました。

以下に示す JavaScriptコードを追加することで、hx-push-url 属性の動作を維持しつつ、希望する URL形式を browser history API に正しく渡すことができるようになります。

<script>
  addEventListener('htmx:beforeHistoryUpdate', function (event) {
    const proposedUrl = event.detail.history.path;
    const customUrl = proposedUrl.replace(/\/include\/htmx\/.*\.html/, '');
    event.detail.history.path = customUrl;
  });
</script>

処理としては htmx で History を更新する前の path を取得し、/include/htmx/ 〜 .html 部分を削除するという処理を追加しています。

一般的に a-blog cms の部分的なファイルは include ディレクトリに整理して保存するというルールが定着していますので、htmx で読み込むテンプレートファイルは、その include ディレクトリ内に htmx を用意し、その htmx ディレクトリ内で管理というルールでテーマを構築というルールにする事で上記の JavaScript が有効的に動作させることができます。

モジュールIDのキャッシュ

a-blog cms Ver.2.11.0よりモジュールIDのキャッシュ機能が使えるようになりました。a-blog cms のキャッシュ機能は基本的に、ページ単位でキャッシュを行う仕組みになっております。 そのため、URLが少しでも違うとキャッシュが利用されず、そのページにある全てのモジュールが動作してしまいます。ページを構成する要素はたくさんありますが、例えば「グローバルナビゲーション」や「フッター」の情報などは頻繁に変わるものではありません。 情報は変わっていないのに、そのページにある他の要素が更新されたことで、このような更新が少ないモジュールも毎回PHPが動作して生成しなおすのは、パフォーマンス的にあまりよろしくありません。

そこでVer.2.11.0からはページ単位以外に更新頻度が違うモジュールごとにキャッシュができる仕組みを導入しました。

設定方法

管理画面 > モジュール > 該当するモジュールIDに移動します。「条件設定」を開き「キャッシュ」に分単位で数値を設定します。 ここで設定した時間がキャッシュの有効期限となります。


モジュールキャッシュの削除タイミング

モジュールのキャッシュは以下のような条件で削除されるようになっています。

  • 該当のモジュールIDが更新されたタイミング
  • 該当のモジュールIDのキャッシュ時間の設定を経過したタイミング

なお、該当のモジュールIDのテンプレートが更新されたタイミングで別のキャッシュができますがキャッシュ自体が削除されるわけではないのでご注意ください。

Developテーマの使い方


Develop テーマとは、フロントエンドの開発環境が整備された a-blog cms の公式テーマです。

Develop テーマは a-blog cms を開発する際によく使うテンプレートをシンプルにしたものが同梱されており、独自のデザイン・レイアウトでテーマ開発する場合に最適なテーマです。

また、テンプレートは a-blog cms Ver. 3.2 から導入された Twig テンプレートを利用して記述されており、より保守性・再利用性に優れたテーマを開発することができます。


ツールについて

Develop テーマでは次のようなツールを導入してしています。

  • Tailwind CSS (ユーティリティファーストCSSフレームワーク)

  • Alpine.js (HTMLに直接振る舞いを記述できるようにするJavaScriptフレームワーク)

  • htmx (HTML の属性だけで Ajax や部分更新、履歴管理といった高度なインタラクションを実現できる軽量な JavaScript ライブラリ)

  • Vite (高速なフロントエンドビルドツール)

また、これらを快適に使用できるように、 EditorConfigESLintPrettierStylelint の設定が組み込まれています。

バンドル環境の使い方

ンストール

themes/develop/ に移動して、下記のコマンドを実行します。実行することでビルドに必要なツールなどがインストールされます。

$ npm install

npm コマンド自体が存在しない場合、Node.js をインストールすることで npm コマンドが有効になります。Node.js はこちらのサイトからインストールできます。

ビルドコマンド

本番用ビルド

cssのbuildとjsのbuildを行います。 納品時にはこのコマンドを実行して納品してください。 JavaScriptが productionビルド となります。

$ npm run build

また、本番ビルドしたCSSとJavaScriptを利用する場合は a-blog cms 設置ディレクトリの .env ファイルで、VITE_ENVIRONMENT に production を指定します。

# Vite
VITE_ENVIRONMENT=production # development | production

開発用ビルド

以下のコマンドを実行すると、cssとjsの変更をwatchしてビルドを行います。余分なコードが入ったり、最適化されないため納品時には必ず npm run build しましょう。

$ npm run dev

また、開発用にビルドしたCSSとJavaScriptを利用する場合は a-blog cms 設置ディレクトリの .env ファイルで、VITE_ENVIRONMENT に development を指定します。

# Vite
VITE_ENVIRONMENT=development # development | production

Vite 連携について詳しくはドキュメントを御覧ください。

組み込みJSの読み込みについて

JavaScriptは、include/head/js.twig で読んでいます。 Develop テーマではパフォーマンス向上のために、Touch_SessionWithContributionを使って、投稿者以上以上の場合だけ読み込むようにしています。

{% if touch('Touch_SessionWithContribution') %}
<script src="{{ JS_LIB_JQUERY_DIR }}jquery-{{JS_LIB_JQUERY_DIR_VERSION}}.min.js" charset="UTF-8"></script>
<script src="{{ ROOT_DIR }}acms.js{{ js.arguments }}" charset="UTF-8" id="acms-js"></script>
{% endif %}

組み込みJSを読まないようにすると、スライダーや、画像ビューワーなどの組み込みJSが動作しなくなりますが、一部のよく利用する組み込みJSをsrc/js/lib/buildIn/に実装し、src/js/main.jsで読み込んでいます。

import {
  // 組み込みJS
} from './lib/buildIn/';

これ以外に必要な機能やライブラリは、自分でインストール、実装する必要があります。 バンドル環境が整っていますので、 npm経由でライブラリをもってきて、 importする方式をお勧めします。以下 lazy load を実装する例になります。

$ npm i vanilla-lazyload
import LazyLoad from 'vanilla-lazyload';

domContentLoaded(() => {
  new LazyLoad({elements_selector: '.js-lazy-load'});
});