Vite 連携機能


Vite(ヴィート)は、モダンなWeb開発のための高速ビルドツールです。本CMSのテンプレートには、Viteでビルドしたファイルを簡単に読み込むための補助機能が組み込まれています。

使い方

a-blog cms で Vite を利用してバンドルしたファイルを読み込むためには、バックエンドとの統合で記載されている仕様に合わせて、script タグや link タグを出力する必要があります。

Vite 連携機能を使用することで、Vite の仕様に合わせてタグを出力することができるようになり、a-blog cms のテンプレート内で簡単に Vite を利用することができます。

Vite の設定

Vite の設定は vite.config.js に記述します。詳細は Vite のドキュメント を参照してください。

build.manifest を true に設定し、rollupOptions.input でエントリーポイントとなるファイルを指定します。 以下のサンプルは、themes/利用テーマ/src/js/main.js をエントリーポイントとして指定しています。

import { defineConfig } from 'vite';

export default defineConfig({
    // ...
    build: {
        manifest: true,
        rollupOptions: {
            input: {
                bundle: resolve(__dirname, 'src/js/main.js'),
            },
        },
    },
});

この設定をすることで、manifest.json にビルドされたアセットのパスが出力されるようになります。

スクリプトとスタイルの読み込み

Vite の設定が完了したら、a-blog cms のテンプレート内にスクリプトとスタイルを読み込むための記述を追加します。 head タグ内 @vite() を追加します。

<!DOCTYPE html>
<head>
  @vite('src/js/main.js')
</head>

複数のエントリーポイントを指定する場合は、配列で指定します。

<!DOCTYPE html>
<head>
  @vite(['src/js/main.js', 'src/js/main.css'])
</head>

twig テンプレートを利用している場合は vite 関数を利用できます。

<!DOCTYPE html>
<head>
  {{ vite('src/js/main.js') }}
</head>

twig で vite 関数を利用している場合に複数のエントリーポイントを指定する場合は、配列で指定します。

<!DOCTYPE html>
<head>
  {{ vite(['src/js/main.js', 'src/js/main.css']) }}
</head>

最後に、a-blog cms 設置ディレクトリにある .env ファイルを編集します。

以下のように VITE_ENVIRONMENT に development を指定します。

# Vite
VITE_ENVIRONMENT=development # development | production

これで、Vite の開発サーバーからビルドされたアセットが読み込まれるようになります。

本番環境での利用

Vite で本番環境用ビルドしたアセットを読み込めるようにするためには、a-blog cms 設置ディレクトリにある .env ファイルを編集します。

以下のように VITE_ENVIRONMENT に production を指定します。

# Vite
VITE_ENVIRONMENT=production # development | production

これで、Vite で本番環境用ビルドしたアセットが読み込まれるようになります。

設定

@vite() の第2引数にJSON形式でオプションを指定することができます。

出力ディレクトリの設定

例えば、Vite の設定で build.outDir を bundle に設定している場合、以下のように記述します。

import { defineConfig } from 'vite';

export default defineConfig({
    // ...
    build: {
        outDir: 'bundle',
    },
});

@vite() の第2引数に出力ディレクトリを指定します。

<!DOCTYPE html>
<head>
  @vite('src/js/main.js', {
    "outDir": "bundle"
  })
</head>

twig テンプレートを利用している場合は以下のように記述します。

<!DOCTYPE html>
<head>
  {{ vite('src/js/main.js', {
    outDir: 'bundle'
  }) }}
</head>

タグの属性をカスタマイズする

タグの属性をカスタマイズすることができます。

以下の例では、エントリーポイントから出力された script タグに async 属性を追加しています。

<!DOCTYPE html>
<head>
  @vite('src/js/main.js', {
    "scriptTagAttributes": {
      "async": true
    }
  })
</head>

また、link タグの属性もカスタマイズすることができます。 以下の例では、エントリーポイントから出力された link タグに type="text/css" 属性を追加しています。

<!DOCTYPE html>
<head>
  @vite('src/styles/main.css', {
    "linkTagAttributes": {
      "type": "text/css"
    }
  })
</head>

twig テンプレートを利用している場合は、以下になります。

<!DOCTYPE html>
<head>
  {{ vite('src/js/main.js', {
    scriptTagAttributes: {
      async: true
    }
  }) }}
</head>
<!DOCTYPE html>
<head>
  {{ vite('src/styles/main.css', {
    linkTagAttributes: {
      type: "text/css"
    }
  }) }}
</head>

React の利用

React と @vitejs/plugin-react を利用する場合、既存の @vite と一緒に、追加で @viteReactRefresh を追加する必要があります。

<!DOCTYPE html>
<head>
  @viteReactRefresh
  @vite('src/js/main.jsx')
</head>

@viteReactRefresh は、React の Hot Module Replacement を有効にするためのスクリプトを出力します。

<script type="module">
  import RefreshRuntime from 'http://localhost:5173/@react-refresh'
  RefreshRuntime.injectIntoGlobalHook(window)
  window.$RefreshReg$ = () => {}
  window.$RefreshSig$ = () => (type) => type
  window.__vite_plugin_react_preamble_installed__ = true
</script>

twig テンプレートを利用している場合は、以下のように記述します。

<!DOCTYPE html>
<head>
  {{ viteReactRefresh() }}
  {{ vite('src/js/main.jsx') }}
</head>

グローバル変数

Vite 連携時の状態については、グローバル変数を用いて確認できます。

グローバル変数名

説明

VITE_ENVIRONMENT

現在の Vite の動作モードの値を出力します(development または production)です。

development の場合、Vite の開発サーバーを利用し、production の場合はビルドされたアセットを利用します。

VITE_DEV_SERVER_URL

Vite の開発サーバーの URL を出力します。

高度なカスタマイズ

環境変数を利用して、Vite の設定をカスタマイズすることができます。

a-blog cms設置ディレクトリにある .env ファイルを編集することで環境変数を設定します。

# Vite
VITE_ENVIRONMENT=production # development | production
VITE_MANIFEST_PATH=dist/.vite/manifest.json
VITE_DEV_SERVER_URL=http://localhost:5173

以下、環境変数の説明です。

変数名

説明

VITE_ENVIRONMENT

development または production を指定します。

development の場合、Vite の開発サーバーを利用し、production の場合はビルドされたアセットを利用します。

VITE_MANIFEST_PATH

manifest.json のパスを利用しているテーマディレクトリから指定します。

VITE_DEV_SERVER_URL

Vite の開発サーバーの URL を指定します。

ページルーティングの概要


本CMSでは、URLの構造ブログ・カテゴリー・エントリーの設定を元に、表示するテンプレートが自動的に決定されます。
これにより、URL設計とテンプレート設計を連動させることができ、複数ブログや多階層カテゴリーでも柔軟なページ構成が可能です。

ルーティングの流れ

  • URL解析

    • リクエストされたURLから、ブログ・カテゴリー・エントリーなどを特定します。

  • テーマの決定

    • 特定したブログのテーマ設定からテーマを確定します。

  • ページタイプの判定

    • 特定したデータ情報・URLから、トップページ、一覧ページ、エントリー詳細ページなど、ページタイプを決定します。

  • テンプレートの探索

    • ページタイプに応じたテンプレートを、確定したテーマのフォルダ内から優先順に探索します。

  • テンプレートの読み込み

    • 該当テンプレートを読み込み、モジュール・変数を解決してHTMLを生成します。

ページタイプ


テーマフォルダが確定した後は、URL情報をもとにページタイプを判定します。

ページタイプには例えば、以下のような種類があります。

  • トップページ

  • 一覧ページ

  • 詳細ページ

  • 404ページ

  • 管理ページ

ページタイプ例

トップページ

トップページは、各ブログに1ページのみ存在するページタイプです。

トップページの判定例1

ブログの設定でドメインが example.com、ブログコードが空の場合、次のURLでのみトップページと判定されます。

https://example.com

※ クエリパラメータやハッシュ(#以降)は対象外となります。

トップページの判定例2

ブログの設定でドメインが example.com、ブログコードが newsの場合、次のURLでのみトップページと判定されます。

https://example.com/news/

詳細ページ

詳細ページは、CMSのエントリー情報が一意に決まっている時のページタイプです。基本 xxxx.html で終わるURLがこれに該当します。

詳細ページの判定例1

CMSで管理している、エントリーのコード(ファイル名)と同一のURLがこれに該当します。

https://example.com/news/sample.html

※ クエリパラメータやハッシュ(#以降)は対象外となります。

詳細ページの判定例2

例外として、CMSで管理しているエントリーのコード(ファイル名)が空の場合は xxxx.html で終わらないURLになります。

news ブログ or news カテゴリーにエントリーコードが空のエントリーがある場合は、以下URLのページタイプは詳細ページになります。

https://example.com/news/

一覧ページ

一覧ページは、URL上の情報で絞り込みが行われた場合に表示されるページタイプです。
ただし、詳細ページに該当する場合は詳細ページが優先されます

一覧ページの判定例

以下のように、URLでカテゴリ・カスタムフィールド・日時・キーワード・タグなどを指定している場合、一覧ページとして扱われます。複数の条件を組み合わせた絞り込みも対象です。

カテゴリー一覧
https://example.com/news/

カスタムフィールドで検索
https://example.com/field/price/lt/3000

日時で検索
https://example.com/2025/09/

キーワードで検索
https://example.com/keyword/検索ワード/

タグで検索
https://example.com/tag/タグA/タグB/

※ クエリパラメータやハッシュ(#以降)は対象外となります。


404ページ

404ページは、URLの情報からブログ・カテゴリー・エントリーなどが特定できなかった場合かつ、固定テンプレートが存在しない場合(後述)に表示されるページタイプです。

404ページの判定例

存在しないブログ・カテゴリーを指定
https://example.com/hoge/

存在しないエントリーを指定
https://example.com/hoge.html

勘違いしやすいところとして、標準で用意されている絞り込みのURL情報でヒットがない場合には404ページタイプとはなりません

カスタムフィールドで検索でヒットしない
https://example.com/field/price/lt/99999

日時で検索でヒットしない
https://example.com/2099/12/01

キーワードで検索でヒットしない
https://example.com/keyword/hogehogehoge/

タグで検索でヒットしない
https://example.com/tag/タグAAAAAAAAAAAAA/

管理ページ

管理ページは、URLが管理ページのURLだった場合に選択されるページタイプです。基本的にはURLに /admin/xxxxx/ というパスがあると管理ページと認識されます。

管理ページの判定例

https://example.com/bid/1/admin/top/

他にもページタイプによって選択されるテンプレートはありますが、ここでの説明では省略します。

他テンプレートの例

  • サインイン画面 _member/signin.html

  • サインアップ画面 _member/signup.html

  • パスワード再発行画面 _member/reset-password.html

  • プロフィール更新画面 _member/update-profile.html

  • パスワード変更画面 _member/update-password.html

  • 退会画面 _member/withdrawal.html

  • 404エラー画面 404.html

  • 500エラー画面 500.html


ページタイプとテンプレートファイル設定

各ページタイプごとに、使用されるテンプレートファイルを設定できます。設定は 管理ページ > テーマ > テーマ設定 から行います。

テーマ設定画面で、各ページタイプ毎にテンプレートファイルを指定します。
テーマ設定画面

テーマディレクトリの選択

  • テーマディレクトリ名には、現在使用しているテーマ名が表示されます。

  • 本CMS設置ディレクトリ内の themes/ 以下にあるテーマディレクトリが、プルダウンメニューから選択できます。

template.yaml の利用

  • テーマディレクトリ内に template.yaml が存在する場合、「テンプレート設定を有効化」 することで、template.yaml に記述されたテンプレート設定が優先して読み込まれます。

  • template.yaml は、テーマを使いまわす場合に毎回テンプレートファイルを個別設定する手間を省くために利用します。

tpl_top    : index.html
tpl_index  : index.html
tpl_detail : _entry.html
tpl_404    : 404.html
tpl_admin  : admin.html
tpl_login  : _member-admin/login.html

設定の効果

このようにテーマ設定を通じて、各ページタイプごとに表示されるテンプレートファイルを柔軟に切り替えることが可能です。たとえば、以下のようにページタイプごとに異なるテンプレートを割り当てられます。

  • トップページ → _top.html

  • 一覧ページ → index.html

  • 詳細ページ → _entry.html

  • エラーページ → 404.html

  • 管理ページ → admin.html


テーマセット機能は Ver. 3.1 から導入された機能のため、Ver. 3.0 以前のバージョンを利用の方は 管理ページ > コンフィグ > テーマ設定(レガシー設定) からテーマの変更ができます。

利用テンプレートの確認方法


サイト制作を進めていくと、テンプレートファイルは増えていきます。
数が多くなると「どのテンプレートが使われているのか分からない」「他の人が作ったテーマを調べるのに時間がかかる」といった課題が出てきます。

ここでは、目的のテンプレートを素早く見つける方法を紹介します。

下準備

まずはテンプレート情報を表示できるように設定を行います。

1. HTML コメントを有効にする

管理画面の 「管理ページ > コンフィグ > 出力設定」 で、
「HTMLコメントの削除」 にチェックが入っている場合は外してください。

これで、インクルードされたファイルの情報が HTML コメントとして出力されるようになります。

出力設定管理画面
出力設定管理画面

2. DEBUG_MODE を有効化する

管理者でCMSにログインし、プロフィール変更ページに移動します。

プロフィール変更ページへ移動
プロフィール変更ページへ移動

プロフィール変更ページで モードデバッグモード に変更します。

デバッグモードに変更
デバッグモードに変更

テンプレート確認方法

1. 使用中のテンプレートを確認する

管理画面にログインした状態で、調べたいページにアクセスすると、
画面右上の 管理ボックス に「現在表示中のテンプレート」が表示されます。

管理ボックスで、テンプレートパスを確認
管理ボックス

これで、まずはどのテンプレートが呼び出されているか確認できます。


2. インクルードファイルを確認する

多くの場合、直接表示されているテンプレートの中でさらに インクルードされたテンプレート が使われています。

ソースコードを確認すると、以下のように HTML コメントでインクルードファイルのパスが出力されます。

<!-- Start of include : source=themes/site/include/parts/message-top.html -->
...
<!-- End of include : source=themes/site/include/parts/message-top.html -->

これにより、どのテンプレートファイルが呼び出されているのかを正確に把握できるようになります。

慣れないうちは、この機能を利用して テンプレートの全体像を把握する のがおすすめです。

hx-get の利用


hx-get は、リンクやボタンなどの要素から GET リクエストを発行し、返ってきた HTML をページの一部に差し替えるための属性です。ページ全体のリロードを避けつつ、一覧の追加読み込み、タブ切り替え、カレンダーの月送りなどを実装できます。

a-blog cms Ver. 3.2 では標準の 組み込み JavaScript に htmx が含まれているため、hx-get を記述するだけで利用できます。詳細は「事前準備について」を参照してください。

基本構文

<a href="{url}" 
   hx-get="{url}tpl/include/entry/summary.html"
   hx-target="this"
   hx-swap="outerHTML"
   hx-push-url="{url}">リンク</a>

より実践的にイメージしやすいように a-blog cms の変数 {url} を記述して例をあげています。

通常 {url} には、リンク先となるページの URL が入ります。a-blog cms が生成する URL は、カテゴリーやエントリー、カスタムフィールドの条件などを組み合わせた URLコンテキスト と呼ばれる形式になっています。URLコンテキストとは、URLによって表現されるページの文脈情報のことで、この URL 自体が表示する内容の条件を表しています。

そして、その URL の後ろに /tpl/ を付与し、さらに include/entry/summary.html のようなテンプレートパスを指定することで、同じ条件で取得したコンテンツを、指定したレイアウトで部分的に置き換えることができます。

hx-targethx-swap では特別な設定を行っていないため、ここでの説明は省略します。

hx-push-url は、部分更新を行った際にブラウザのアドレスバーや履歴(History API)を更新するための属性です。公式ドキュメントでは hx-push-url="true" のように指定する例が紹介されていますが、この場合は hx-get に指定した URL がそのまま履歴に記録され、例えば https://example.com/tpl/entry/summary.html のように tpl パス付きの URL になってしまいます。

a-blog cms では、hx-push-url実際のページ URL(a タグのリンク先) を指定することで、htmx が使われなかった場合の通常遷移と同じ URL を履歴に残すことができます

部分更新後も履歴や URL 表示がユーザーにとって自然な形となり、戻る・進むの操作やブックマーク時の挙動も通常遷移と同様になります。


hx-get 実装例


siteテーマ「お知らせ」の一覧(もっと見る)ボタン

お知らせ一覧ページ にある、最も一般的な「もっと見る」ボタンの実装例です。

通常の Entry_Summary モジュールではページャー部分に /include/parts/pager.html をインクルードしますが、「お知らせ」の部分だけは以下の pager-read-more.html を読み込むようにしています。

/include/parts/pager-read-more.html

<!-- BEGIN pager:veil -->
<!-- BEGIN forwardLink -->
<div id="search-results">
  <div class="read-more-pager">
    <a href="{url}" class="button is-width-lg"
    hx-get="{url}tpl/include/htmx/entry/summary-more.html"
    hx-target="#search-results"
    hx-swap="outerHTML">もっと見る</a>
  </div>
</div>
<!-- END forwardLink -->
<!-- END pager:veil -->

この例では、{url} にページャーの次ページ URL が入り、その後ろに /tpl/include/htmx/entry/summary-more.html を付けて、次ページの一覧部分だけを返すテンプレートを指定しています。

hx-target="#search-results" としているため、このラッパー要素全体を hx-swap="outerHTML" で置き換えます。次ページにも同じ構造の「もっと見る」ボタンが含まれるので、連続クリックでページを進められます。


「よくある質問」のアコーディオン内の A コンテンツ読み込み

これまでは post include を利用し、質問(Q)部分に <form> タグを設置して、回答(A)を非同期で読み込んでいました。

a-blog cms Ver. 3.2 から標準搭載された htmx により、hx-get が利用できるようになったため、<a> タグでよりシンプルに読み込みが可能になりました。

変更前(post include 利用)

<form action="{url}" method="post" class="js-post_include" target="#jsIncludeTo{eid}">
  <h3 class="faq-question">
    <button type="submit" name="ACMS_POST_2GET_Ajax" class="toggle-button faq-question-button js-toggle-button" aria-expanded="false" aria-controls="answer{eid}">
      <span class="faq-icon">Q</span>
      <span class="faq-title">{title}</span>
      <span class="toggle-icon" aria-hidden="false">
        <img src="/images/arrow-down-gray.svg" width="18" height="18" alt="">
      </span>
    </button>
  </h3>
  <input type="hidden" name="tpl" value="faq/post-entry.html" />
</form>
<div id="answer{eid}" class="toggle-body js-toggle-body" aria-hidden="true">
  <div class="faq-answer">
    <p class="faq-icon"><a href="{url}">A</a></p>
    <div class="faq-body">
      <div id="jsIncludeTo{eid}"></div>
    </div>
  </div>
</div>

変更後(htmx 利用)

<a href="{url}" class="toggle-button faq-question-button js-toggle-button"
   hx-trigger="click" hx-get="{url}/tpl/include/htmx/entry/body-faq.html" 
   hx-target="#jsIncludeTo{eid}" 
   hx-swap="innerHTML">
  <span class="faq-icon">Q</span>
  <span class="faq-title">{title}</span>
</a>
<div id="answer{eid}" class="toggle-body js-toggle-body">
  <div class="faq-answer">
    <p class="faq-icon">A</p>
    <div class="faq-body">
      <div id="jsIncludeTo{eid}"></div>
    </div>
  </div>
</div>

このように hx-get を使うことで、フォーム構造を使わずにリンクだけで部分更新が実現でき、マークアップとコードの両方を簡潔にできます。


「ブログ」のカレンダーの月送り

a-blog cms の標準動作では、月送りリンクをクリックすると URL に /YYYY/MM/URLコンテキスト が付与され、サブカラムのカレンダーだけでなくメインコンテンツまでその月の表示に切り替わってしまいます。サブカラムのカレンダーだけを更新したい場合、この挙動は望ましくありません。

そこで htmx を使い、hx-get でカレンダー部分の断片だけを取得して差し替えることで、メインの表示状態を保持したままカレンダーのみを更新します。この場合には、hx-push-url 属性は利用しないようにしておきます。

blog@site/include/calendar/month.html

<!-- BEGIN_MODULE Calendar_Month id="{{module_id}}" -->
<section class="section-side" id="calendar">

<!-- ナビゲーション部分だけ -->
<a href="{url}" hx-target="#calendar" hx-get="{prevUrl}tpl/include/calendar/month.html" hx-swap="outerHTML">←</a>
<a href="{url}" hx-target="#calendar" hx-get="{nextUrl}tpl/include/calendar/month.html" hx-swap="outerHTML">→</a>

</section>
<!-- END_MODULE Calendar_Month -->

この例では、hx-get/tpl/include/calendar/month.html を指定することで、クリック時にカレンダー断片だけを取得して #calendar に差し替えています。これにより、メインコンテンツを変えずにサブカラムのカレンダーだけを更新 できます。


siteテーマ「キーワード検索」ページでのイクリメンタルサーチ

標準ではオフのため、利用する場合は site/index.html のインクルード先を次のように変更します。
変更前:@include("/include/parts/search-general.html")
変更後:@include("/include/htmx/parts/search-general.html")

この切り替えにより、フッターの キーワード検索フォーム が htmx 対応版に変わり、入力のたびに結果が更新される インクリメンタルサーチ が有効になります。

<input type="search" id="search-{{search-id}}" name="keyword" value="%{KEYWORD}" size="15" class="form-search-input" placeholder="キーワードを入力"
hx-get="/include/htmx/entry/summary-list.html" hx-trigger="keyup changed delay:500ms" hx-target="#summary_list">

フォーム本体では <form>hx-post の設定を行っていますが、インクリメンタルサーチは 入力欄のイベント によって hx-get が発火する実装です。hx-trigger="keyup changed delay:500ms" により、キー入力の確定や値の変更から 500ms 後にリクエストが送られ、#summary_list に差し替えられます。Enter での確定やボタン送信時は、従来どおりフォームの hx-post が動作し、結果表示用テンプレートに基づいた部分更新が実行されます。