WordPressの固定ページでVue CLIのホットリロード環境を構築する

このサイトで公開している画像サイズ計算機はVueで作りましたが、WPテーマのcssバッティングやレスポンシブの確認などがあるため、どうしてもWPの固定ページでVue CLIのホットリロード(HMR)を実現する必要がありました。

今となっては「まぁそう作ればいいよね」ぐらいの感覚ですが、頭が固くなったのか環境を作るのに手間取ってしまったので、忘れないように構築手順をできるだけ詳しく書いていきます。
※ ディレクトリ、ファイルpathは少し省略しているので読みにくいかもしれません。

目次

全体構成

下図の構成でホットリロードをできるようにします。

Vueプロジェクトの proj-hot-reload ディレクトリは、公開するものではないので htdocs ディレクトリと同列にしています。

Vueプロジェクトのビルド先は、管理のしやすさを優先してWPテーマのchild/pages内に作成するようにしました。

child/page-hot-reload.phpは、WP固定ページのURLスラッグに hot-reload を設定して自動的に読み込ませつつ、requireでビルドされた child/pages/build-hot-reload/index.php を読み込むようにします。

Vueプロジェクトの作成とビルド設定

ルートディレクトリにVueプロジェクトを作成するので、ターミナルからVueプロジェクト作成コマンドを実行します。
プロジェクト名は proj-hot-reload で、Vueのバージョンは 2 にします。

# プロジェクト作成コマンド
vue create proj-hot-reload

# 以下プロジェクト作成コマンド実行後に表示される選択肢
Vue CLI v4.5.13
? Please pick a preset:
❯ Default ([Vue 2] babel, eslint) # ここを選択
  Default (Vue 3) ([Vue 3] babel, eslint)
  Manually select features

次に作成したVueプロジェクト proj-hot-reload/public ディレクトリに index.php を作成します。
この index.php はWordPressの固定ページで表示させつつ、Vueを動作させるので最低限必要なコードは以下のようになります。

<?php
add_action('wp_enqueue_scripts', function () {
  $output_dir = '/wp-content/themes/child/pages/build-hot-reload';
  $css_index = "{$output_dir}/css/index.css";
  $js_chunk_vendors = "{$output_dir}/js/chunk-vendors.js";
  $js_index = "{$output_dir}/js/index.js";

  wp_enqueue_style('app-index', $css_index);
  wp_enqueue_script('app-chunk-vendor', $js_chunk_vendors, array(), false, true);
  wp_enqueue_script('app-index', $js_index, array(), false, true);
});

get_header();

while (have_posts()) :
  the_post();
  ?>
  <div id="app"></div>
  <?php
endwhile;

get_footer();

次はVueプロジェクトのビルド先を child/pages ディレクトリにするための設定を行います。
Vueプロジェクトの proj-hot-reload/vue.config.js を作成して以下の設定を入れます。

module.exports = {
  pages: {
    index: {
      entry: 'src/main.js',
      template: 'public/index.php',
      filename: 'index.php',
      minify: false,
      inject: false,
    }
  },

  productionSourceMap: false,
  filenameHashing: false,
  outputDir: '../htdocs/wp-content/themes/child/pages/build-hot-reload',
  publicPath: '/wp-content/themes/child/pages/build-hot-reload',
}

設定内容の細かい説明は省きますが、phpファイルのミニファイや、jsやcssを自動で書きこんでくれる機能をOFFにしています。

ビルドしてWPテーマ内に出力されるか確認します。

cd proj-hot-reload

# yarnの場合
yarn build

# npmの場合
npm run build

WPテーマ内に出力されたindex.php(child/pages/build-hot-reload/index.php)が、Vueプロジェクトのindex.php(proj-hot-reload/public/index.php)と同じ内容になっているか確認してください。

正常に出力されない場合はoutputDirの部分が間違っている可能性があるので見直してください。

WPの固定ページ作成

Vueプロジェクトの出力は完了したので、WPの固定ページを作成します。
管理画面の固定ページから新規作成を行い、URLスラッグを hot-reload にして公開します。

このURLスラッグのページを表示するため child/page-hot-reload.php を作成します。
ファイルの内容はビルドされた child/pages/build-hot-relaod/index.php を読み込むようにします。

<?php require_once __DIR__ . '/pages/build-hot-reload/index.php'; ?>

環境によりますが、http://ホスト/hot-reload/ でアクセスして下図のようにVueのロゴが出ていれば正常に動作しています。

文字は表示されるけどロゴが表示されない場合は、vue.config.jsのpublicPathが間違っている可能性があるので見直してください。

ホットリロード設定

ホットリロードさせるにはVue CLIのserveコマンドを実行して、ホットリロードサーバーを起動しておく必要があります。
build-hot-reload/index.phpに書いているjsとcssの読み込み先をホットリロードサーバーに向けるといいので、以下2つのファイルを編集します。

  1. proj-hot-reload/vue.config.js
  2. build-hot-reload/index.php

proj-hot-reload/vue.config.js の編集

proj-hot-reload/vue.config.jsを開いてファイル内容を変更します。

module.exports = {
  ...
  publicPath: '//localhost:8889',
  
  devServer: {
    port: 8889,
    disableHostCheck: true,
    headers: {
      'Access-Control-Allow-Origin': '*'
    },
  },
}

publicPathを //localhost:8889 に変更して devServerの情報を追記しました。port番号はとりあえず空いてそうなのを選んでいます。

試しにVue CLIのserveコマンドを実行してホットリロードサーバーを起動します。

# yarnの場合
yarn serve

# npmの場合
npm run serve

# serve起動結果
  App running at:
  - Local:   http://localhost:8889/
  - Network: http://192.168.184.174:8889/

正常に起動すると、Local / Network のURLが表示されます。ポートが塞がっている場合は別のポート番号が表示されるので、設定ファイルのポート番号を調整したほうがスムーズに作業できると思います。

build-hot-reload/index.phpの編集

ホットリロードサーバーを起動して表示されたLocalのURLをbuild-hot-reload/index.phpに設定します。
大本である proj-hot-reload/public/index.php を編集してもいいですが、毎回ビルドするのが手間なのでビルド結果の build-hot-reload/index.php を調整して、ホットリロードが成功したらソースコードをproj-hot-reload/public/index.phpにコピペします。

<?php
add_action('wp_enqueue_scripts', function () {
  $output_dir = '//localhost:8889';

  $css_index = "{$output_dir}/css/index.css";
  $js_chunk_vendors = "{$output_dir}/js/chunk-vendors.js";
  $js_index = "{$output_dir}/js/index.js";

...

$output_dir を変更しました。
ブラウザで http://ホスト/hot-reload/ にアクセスして正常に表示されるか確認します。Vueのロゴが表示されていれば、jsやcssは http://localhost:8889 から読み込まれていることになります。
ブラウザがchromeなら要素の検証で読み込み先を確認したほうが正確です。

ホットリロードの確認

ホットリロードできるか確認を行うために proj-hot-reload/src/App.vue の一部を書き換えて保存してみます。

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Weeeeeeeeelcome to Your Vue.js App"/>
  </div>
</template>

下図のようになれば成功です。

ホットリロードが成功したので build-hot-reload/index.php のソースを proj-hot-reload/public/index.php に入れたいところですが、jsとcssの読み込み先が //localhost:8889 になっているためホットリロードサーバーを落とすと何も表示されなくなります。
そこでホットリロードサーバーが起動している場合のみ //localhost:8889 が設定されるように変更します。

ホットリロードサーバーが起動しているか自動判別

ホットリロードサーバーは //localhost:8889 で起動しているので、phpのfsockopenでアクセスしてホットリロードサーバーが起動しているか確認します。

build-hot-reload/index.php を編集します。

<?php
add_action('wp_enqueue_scripts', function () {

  function is_fsockopen($host, $port){
    $fp = @fsockopen($host, $port, $error_code, $err_message, 0.1);
    if ($fp) {
      fclose($fp);
      return true;
    } else {
      return false;
    }
  }
  
  $host = 'localhost';
  $port = '8889';
  $running_hmr_serve = $_SERVER['HTTP_HOST'] == 'hkoj-wptest.hakooji.local'
                       && is_fsockopen($host, $port);

  $output_dir = '/wp-content/themes/child/pages/build-hot-reload';
  if ($running_hmr_serve) {
    $output_dir = "//{$host}:{$port}";
  }

  $css_index = "{$output_dir}/css/index.css";
  $js_chunk_vendors = "{$output_dir}/js/chunk-vendors.js";
  $js_index = "{$output_dir}/js/index.js";

  wp_enqueue_style('app-index', $css_index);
  wp_enqueue_script('app-chunk-vendor', $js_chunk_vendors, array(), false, true);
  wp_enqueue_script('app-index', $js_index, array(), false, true);
});

get_header();

while (have_posts()) :
  the_post();
  ?>
  <div id="app"></div>
  <?php
endwhile;

get_footer();

16行目でホスト名チェックとis_fsockopen関数でホットリロードサーバーとの接続確認を行い、最後にif文で $output_dir を変更しています。ホスト名チェックは本番サーバーで動かしているときに誤作動しないように入れています。
(話を分かりやすくするために全ての処理をこのファイルに書きましたが、functions.phpで管理するようにして、$running_hmr_serve変数をdefineにしたらスッキリするかなと思います)

ブラウザから http://ホスト/hot-reload/ にアクセスして、ホットリロードサーバーの起動中/停止中に関係なく正常に表示できれば、ソースコードを proj-hot-reload/public/index.php に張り付けます。

ビルドの自動判別

前述で proj-hot-reload/vue.config.js の publicPathを //localhost:8889 にしましたが、このままビルドしてしまうとホットリロードサーバーが起動していないと何も表示されないため、こちらも自動判別処理を追加します。

module.exports = {
  pages: {
    index: {
      entry: 'src/main.js',
      template: 'public/index.php',
      filename: 'index.php',
      minify: false,
      inject: false,
    }
  },

  productionSourceMap: false,
  filenameHashing: false,
  outputDir: '../htdocs/wp-content/themes/child/pages/build-hot-reload',
  publicPath: process.env.NODE_ENV === 'production' ?
    '/wp-content/themes/child/pages/build-hot-reload' :
    '//localhost:8889',

  devServer: {
    port: 8889,
    disableHostCheck: true,
    headers: {
      'Access-Control-Allow-Origin': '*'
    },
  },
}

publicPathをprocess.env.NODE_ENVで分岐するようにしました。

これでホットリロードの環境は完成です!ビルドして動作を確認してみてください。

# yarnの場合
yarn build

# npmの場合
npm run build

おわりに

長くなりましたがWordPressの固定ページでVue CLIのホットリロードを実現できました。
完成図を簡素に描いてみましたがこんな感じでしょうか…。

この構成を思いつく前はvue.config.jsのdevServerにproxyを設定したりと色々やっていましたが、結局phpで切り分ければ手っ取り早いことに気が付きました。
proj-hot-reload/public/index.php を変更してもホットリロードはできませんが、あまり触るファイルではないので問題ないかなというところです。

今回作った構成は以下からダウンロードできます。
※ 親テーマが入っていません。proj-hot-reload/public/index.php の16行目のホスト名を設定する必要があります。


  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次