PR
DEV

ViteでWordPressテーマのHMRを実現する【爆速CSS反映】

【爆速CSS反映】 ViteでWordPressテーマの HMRを実現する DEV

WordPressのテーマ開発で、SCSSを保存するたびにブラウザをリロードして確認していませんか。Gulpのwatch、BrowserSyncのプロキシ設定、webpackの複雑なconfig……。そういった「回り道」なしに、保存した瞬間にスタイルがブラウザに反映される体験を、Viteなら数分で手に入れられます。

この記事では、既存のWordPressテーマ(子テーマ含む)にViteを組み込み、CSSのHMR(Hot Module Replacement)を実現する手順を解説します。筆者が実際に運用しているCocoon子テーマの構成をベースにしているので、そのまま再現できます。

なぜWordPress開発にViteなのか

WordPressのテーマ開発では、PHPでHTMLを出力し、CSSとJavaScriptでフロントエンドを制御します。この構成にモダンなビルドツールを組み合わせたいとき、多くの記事ではwebpackやGulpが紹介されてきました。

Viteはこれらのツールとはアプローチが根本的に異なります。開発時はESM(ES Modules)ベースで個々のファイルをそのまま配信し、本番ビルド時だけRollupでバンドルします。つまり、開発中にバンドル処理が走らないのです。

これが何を意味するかというと、ファイル数が増えても開発サーバーの起動が遅くならないということです。webpackでは依存関係を辿ってバンドルを生成してからサーバーが立ち上がりますが、Viteはサーバーを即座に起動し、リクエストされたファイルだけをオンデマンドで変換します。

HMRが爆速な理由

ViteのHMR(Hot Module Replacement)は文字通り爆速です。SCSSを1行直すと、ページ全体のリロードなしに変更部分だけがブラウザに差し替わります。体感で言えば、保存のCtrl+Sを離す前にブラウザの表示が変わっているレベルです。

従来のBrowserSyncやGulp + LiveReloadでは、ファイル変更を検知→コンパイル→ブラウザにリロード指示→ページ全体を再読み込み、という流れでした。Viteはこのうち「コンパイル→ブラウザにリロード指示→ページ再読み込み」の部分を、WebSocket経由の差分注入に置き換えます。ページ全体の再読み込みが不要なので、スクロール位置もフォームの入力状態も維持されたまま見た目だけが更新されます。

webpackやGulpとの違い

項目VitewebpackGulp + BrowserSync
初回起動数百ms数秒〜十数秒数秒
HMR(CSS変更時)即時(ms単位)1〜3秒リロード(1〜3秒)
設定ファイルの複雑さシンプル複雑(loader/plugin多数)中程度
ESM対応ネイティブトランスパイル経由非対応
本番ビルドRollup(高速)webpack(安定)手動タスク設計

構築に必要なもの

今回の構成で必要なものは以下のとおりです。

  • Node.js(v18以上を推奨)
  • 動作中のWordPressローカル環境(Herd、Local、Docker、wp-envなど何でもOK)
  • テーマディレクトリへのアクセス(子テーマを推奨)

wp-envやDockerを使う方法が一般的に紹介されますが、この記事ではローカル環境の種類を問わない方法を採用しています。Herd + DBnginでも、MAMPでも、Dockerでも同じ手順で導入できます。

ディレクトリ構成

Viteを組み込んだあとの子テーマのディレクトリ構成は次のようになります。

cocoon-child-master/
├── dist/                  # ビルド出力先
│   ├── .vite/
│   │   └── manifest.json  # アセット対応表
│   ├── main-xxxxx.js
│   └── main-xxxxx.css
├── main.js                # エントリーポイント
├── vite.scss              # SCSS(main.jsからimport)
├── vite.config.js         # Vite設定
├── package.json
├── functions.php          # Viteアセットの読み込みロジック
├── style.css              # WPテーマ必須ファイル
└── node_modules/

ポイントは、main.jsがエントリーポイントになっていて、そこからSCSSをimportしている点です。Viteはこのimportチェーンを辿って、CSSもJSもまとめてHMRの対象にします。

STEP 1:Viteのインストールとpackage.json

テーマディレクトリで以下を実行します。

cd wp-content/themes/your-child-theme
npm init -y
npm install --save-dev vite vite-plugin-live-reload vite-plugin-mkcert

インストールするパッケージの役割は次のとおりです。

  • vite:ビルドツール本体
  • vite-plugin-live-reload:PHPファイルの変更時にブラウザをフルリロードするプラグイン
  • vite-plugin-mkcert:ローカル開発サーバーにHTTPS証明書を自動生成するプラグイン

SCSS/Sassを使う場合は追加でSassコンパイラもインストールします。

npm install --save-dev sass

package.jsonのscriptsセクションは次のように設定します。

{
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  }
}

この設定でnpm run devが開発サーバー起動、npm run buildが本番ビルドになります。devスクリプトの改良版は記事の後半で紹介します。

STEP 2:vite.config.jsの設定

Vite設定ファイルの全体像です。1行ずつ意味を解説します。

import { defineConfig } from "vite";
import liveReload from "vite-plugin-live-reload";
import mkcert from "vite-plugin-mkcert";
import path from "path";

export default defineConfig({
  plugins: [
    liveReload(__dirname + "/**/*.php"),
    mkcert(),
  ],
  root: "",
  base: process.env.NODE_ENV === "development"
    ? "https://localhost:5173/"
    : "/wp-content/themes/your-child-theme/dist/",
  build: {
    outDir: path.resolve(__dirname, "./dist"),
    emptyOutDir: true,
    manifest: true,
    rollupOptions: {
      input: path.resolve(__dirname, "main.js"),
    },
    assetsDir: "",
  },
  server: {
    cors: true,
    strictPort: true,
    port: 5173,
    https: true,
    hmr: {
      host: "localhost",
    },
  },
});

各設定項目の解説

設定値役割
pluginsliveReload, mkcertPHP変更でフルリロード、HTTPS対応
base開発時と本番時で切り替えアセットのベースパス。開発時はViteサーバー、本番時はdistディレクトリ
build.manifesttruemanifest.jsonを生成し、PHPからビルド後のファイル名を解決可能にする
build.rollupOptions.inputmain.jsエントリーポイント。ここからimportしたSCSSも含まれる
build.assetsDir“”(空文字)dist直下にファイルを出力(サブディレクトリを作らない)
server.httpstruemkcertと連携してHTTPS開発サーバーを起動
server.hmr.hostlocalhostWebSocket接続先の明示指定。Docker環境等で必要になる場合がある

HTTPSが必要な理由

WordPressサイトがHTTPSで動いている場合(ローカルでも.testドメインでSSLが有効な場合など)、ViteサーバーもHTTPSでないとMixed Contentで接続がブロックされます。vite-plugin-mkcertを使えば自己署名証明書が自動生成され、HTTPS開発サーバーが起動します。

STEP 3:エントリーポイントの作成

main.jsがViteのエントリーポイントです。ここでSCSSをimportします。

// main.js
import './vite.scss';

// ここにJavaScriptのコードを書く
document.addEventListener('DOMContentLoaded', () => {
  console.log('Vite HMR is running!');
});

SCSSファイル(vite.scss)には子テーマのスタイルを記述します。Viteのおかげで、このSCSSを変更するとコンパイル→HMR注入が自動で行われます。

STEP 4:functions.phpでアセットを読み込む

ここが最も重要なパートです。開発中はViteの開発サーバーからアセットを読み込み、本番環境ではビルド済みファイルをmanifest.json経由で読み込む、という切り替えをPHPで行います。

開発環境の判定

まず、Viteの開発サーバーが動いているかどうかを判定する関数を用意します。

define('VITE_SERVER', 'https://localhost:5173');
define('VITE_ENTRY_POINT', '/main.js');

function is_vite_dev() {
  // 本番環境では常にfalseを返す
  $host = $_SERVER['HTTP_HOST'] ?? '';
  if (strpos($host, 'example.com') !== false) {
    return false;
  }

  // wp-config.phpで定義があれば最優先
  if (defined('IS_VITE_DEVELOPMENT')) {
    return IS_VITE_DEVELOPMENT;
  }

  // .hotファイルの有無で判定
  return file_exists(get_theme_file_path('/.hot'));
}

判定方法のポイントは.hotファイルです。Viteの開発サーバー起動時に.hotファイルを作成し、終了時に削除するようにします(後述のdevスクリプト参照)。これにより、開発サーバーが動いているときだけHMRモードになります。

本番ドメインでのガードも入れています。万が一.hotファイルが残ったまま本番にデプロイされても、ドメイン判定で安全にフォールバックします。

アセットの読み込み処理

add_action('wp_enqueue_scripts', function () {

  if (is_vite_dev()) {
    // 開発環境:Vite HMRクライアント + エントリーポイント
    wp_enqueue_script(
      'vite-client',
      VITE_SERVER . '/@vite/client',
      [], null, true
    );
    wp_enqueue_script(
      'vite-main',
      VITE_SERVER . VITE_ENTRY_POINT,
      [], null, true
    );
  } else {
    // 本番環境:manifest.jsonからビルド済みファイルを読み込み
    $manifest_path = get_theme_file_path('/dist/.vite/manifest.json');
    if (!file_exists($manifest_path)) {
      $manifest_path = get_theme_file_path('/dist/manifest.json');
    }

    if (file_exists($manifest_path)) {
      $manifest = json_decode(
        file_get_contents($manifest_path), true
      );

      // CSSの読み込み
      if (isset($manifest['main.js']['css'])) {
        foreach ($manifest['main.js']['css'] as $css_file) {
          wp_enqueue_style(
            'vite-style',
            get_theme_file_uri('/dist/' . $css_file)
          );
        }
      }

      // JSの読み込み
      if (isset($manifest['main.js']['file'])) {
        wp_enqueue_script(
          'vite-main',
          get_theme_file_uri('/dist/' . $manifest['main.js']['file']),
          [], null, true
        );
      }
    }
  }
}, 100);

開発環境では@vite/client(HMR用のWebSocketクライアント)とエントリーポイントの2つを読み込みます。@vite/clientがViteサーバーとの通信を担い、変更があればCSSをインジェクションします。

scriptタグにtype=”module”を追加

ViteのスクリプトはESM形式で配信されるため、<script>タグにtype="module"属性が必要です。WordPressのscript_loader_tagフィルターで追加します。

add_filter('script_loader_tag', function ($tag, $handle, $src) {
  if (in_array($handle, ['vite-client', 'vite-main'])) {
    return '';
  }
  return $tag;
}, 10, 3);

STEP 5:開発サーバーの起動と動作確認

すべての設定が終わったら、開発サーバーを起動します。

npm run dev

ターミナルにViteのロゴとhttps://localhost:5173が表示されたら成功です。この状態でWordPressサイトをブラウザで開くと、Viteの開発サーバー経由でアセットが配信されます。

試しにSCSSファイルの色を変更して保存してみてください。ページをリロードしなくても、ブラウザ上の色が瞬時に変わります。これがHMRの威力です。

PHPファイルの変更時はフルリロード

vite-plugin-live-reloadがPHPファイルの変更を監視しています。functions.phpやテンプレートファイルを編集すると、ブラウザが自動でフルリロードされます。HMRとフルリロードの使い分けが自動で行われるので、開発者は何も意識する必要がありません。

本番ビルドの設定

開発が終わったら本番用ファイルをビルドします。

npm run build

これでdist/ディレクトリにバンドル済みのCSS・JSとmanifest.jsonが出力されます。

dist/
├── .vite/
│   └── manifest.json
├── main-CEBPsstE.js
└── main-CmEZvCsu.css

ファイル名にハッシュが付くのはキャッシュバスティングのためです。ファイル内容が変わればハッシュも変わるので、ブラウザキャッシュが原因で古いCSSが適用される問題を防げます。

WordPressのfunctions.phpでmanifest.jsonを読み込んでいるため、ハッシュが変わってもPHP側の修正は不要です。ビルドするだけで自動的に最新のファイルが参照されます。

実務で差がつくdevスクリプトの工夫

ここまでの基本設定でも十分使えますが、実務で運用すると「開発サーバーを止めたあとにビルドし忘れて、本番に古いCSSが出てしまった」という事故が起きがちです。

これを防ぐために、開発サーバー終了時に自動でビルドが走るdevスクリプトを紹介します。

{
  "scripts": {
    "dev": "bash -lc 'touch .hot; trap \"rm -f .hot; npm run build; trap - EXIT\" INT TERM EXIT; vite'",
    "build": "vite build"
  }
}

このスクリプトが行っていることは次のとおりです。

  1. touch .hot — .hotファイルを作成。functions.phpがHMRモードだと判定する目印になる
  2. trap "rm -f .hot; npm run build; ..." INT TERM EXIT — Ctrl+Cやターミナル終了時のトラップを設定
  3. vite — 開発サーバーを起動
  4. 開発サーバー終了時(Ctrl+C) → .hotファイルを削除 → 自動でビルドを実行

この仕掛けにより、npm run devで開発を始め、Ctrl+Cで止めるだけで常に最新のビルドファイルが残るという運用が実現します。ビルドし忘れの事故がゼロになるので、筆者はこの方法を強く推奨します。

vite.config.jsのカスタマイズ例

LightningCSS でCSSを高速変換する

ViteではCSSトランスフォーマーをLightningCSSに差し替えることで、ベンダープレフィックスの自動付与やネスト構文の変換をネイティブ速度で実行できます。

// vite.config.js に追加
css: {
  transformer: "lightningcss",
},
npm install --save-dev lightningcss

Tailwind CSSとの併用

ViteはTailwind CSSとも相性が良いです。通常のTailwindセットアップ手順に加えて、main.jsで@tailwindディレクティブを含むCSSファイルをimportするだけで動作します。

複数エントリーポイント

フロントエンドと管理画面で異なるスクリプトを使いたい場合は、rollupOptions.inputをオブジェクト形式にします。

rollupOptions: {
  input: {
    main: path.resolve(__dirname, "main.js"),
    admin: path.resolve(__dirname, "admin.js"),
  },
},

よくあるトラブルと対処法

HMRが効かない

  • ブラウザのコンソールを確認して、WebSocket接続エラーが出ていないか確認する
  • HTTPS/HTTP の不一致:WordPressがHTTPSで動いているなら、Viteサーバーもmkcertを使ってHTTPSにする
  • .hotファイルの存在:is_vite_dev()が正しくtrueを返しているか確認する
  • CORSエラー:vite.config.jsのserver.corsがtrueになっているか確認する

ビルド後にCSSが読み込めない

  • dist/.vite/manifest.json(Vite 5以降)またはdist/manifest.jsonが存在するか確認する
  • functions.phpのis_vite_dev()がfalseを返しているか確認する(.hotファイルが残っていないか)
  • baseの本番用パスがテーマディレクトリと一致しているか確認する

CORSエラーが出る

WordPressのドメイン(例:my-site.test)とViteサーバー(localhost:5173)はオリジンが異なるため、デフォルトではCORSで弾かれます。server.cors: trueを設定してください。

.gitignoreの設定

以下のファイル・ディレクトリはGitの管理対象外にしておきましょう。

node_modules/
.hot

dist/をGit管理に含めるかどうかはチームの方針次第です。CIでビルドする場合は除外し、手動デプロイの場合は含めるのが一般的です。

よくある質問

wp-envやDockerでも使えますか?

はい、使えます。ただし、Docker内のWordPressからlocalhostのViteサーバーにアクセスするために、server.hmr.hostの設定やネットワーク構成の調整が必要になる場合があります。Herdのようなネイティブ環境のほうが設定はシンプルです。

jQueryとの共存は可能ですか?

可能です。WordPressが標準で読み込むjQueryはViteの管理外なので、そのまま共存できます。Viteで管理したい場合はimport $ from 'jquery'でインポートすることもできますが、WordPress側のjQueryと二重ロードにならないよう注意してください。

Reactや Vue.jsも使えますか?

ViteはReactやVue.jsの公式ビルドツールでもあるので、もちろん使えます。WordPressのカスタムブロック開発でReactを使う場合にもViteは有力な選択肢です。ただし、WordPress標準の@wordpress/scriptsとは設定体系が異なるため、プラグイン開発では使い分けが必要です。

既存のGulp環境からスムーズに移行できますか?

Gulpの主な用途がSCSS/Sassのコンパイルとブラウザのリロードであれば、Viteへの移行は比較的簡単です。gulpfile.jsの設定をvite.config.jsに置き換え、main.jsからSCSSをimportする構成に変更するだけです。PostCSSプラグインを使っている場合も、ViteはPostCSSをそのままサポートしています。

本番サーバーにNode.jsは必要ですか?

いいえ。Viteはあくまで開発・ビルドツールです。本番環境に必要なのはdist/ディレクトリ内のビルド済みファイルだけで、Node.jsのインストールは不要です。

まとめ

ViteをWordPressテーマに導入する手順を振り返ります。

  1. テーマディレクトリでViteと関連プラグインをインストール
  2. vite.config.jsでエントリーポイント、manifest生成、HTTPS設定を記述
  3. main.jsからSCSSをimportしてエントリーポイントを作成
  4. functions.phpで開発/本番の切り替えロジックとアセット読み込みを実装
  5. npm run devで開発サーバーを起動し、爆速HMRを体験

SCSSの変更が保存した瞬間にブラウザに反映される体験は、一度味わうとGulpやBrowserSyncのリロード待ちには戻れなくなります。設定もファイル数も少なく、既存テーマへの組み込みも容易なので、WordPress開発のフロントエンドを近代化したい方はぜひ試してみてください。

ローカル環境の構築から始めたい方は、Herd + DBnginでWordPressローカル環境を5分で構築【Mac】も参考にしてください。Viteとの相性が良いネイティブPHP環境の構築手順を解説しています。

この記事を書いた人
Naokazu Yokoo

エンジニア歴20年以上。現在はWeb / AI領域で活動中。
開発環境・サーバー環境ともに速度と効率性を追求しています。

Follow Naokazu Yokoo
Follow Naokazu Yokoo

Comment

タイトルとURLをコピーしました