2026年6月23日
2026年6月23日
WordPressのadmin_enqueue_scriptsで管理画面の自作ページだけにVue.jsを読み込む設定
WordPress の管理画面に Vue.js で作った設定画面を載せるには、admin_menu で管理ページを追加し、admin_enqueue_scripts フックで「そのページのときだけ」Vue と自作スクリプトを読み込むのが基本です。管理画面の全ページに Vue を読み込むと、表示が遅くなるだけでなく、他のプラグインとの衝突の原因にもなります。この記事では、フックの引数でページを絞り込む方法、Vue 本体の置き方、PHP から Vue へのデータの渡し方、REST API での保存、つまずきやすい点と確認方法を順に説明します。
全体の流れ
作るものは次の4つです。
admin_menuで管理ページを追加し、Vue をマウントする空の<div>を出力するadmin_enqueue_scriptsで、そのページのときだけ Vue と自作スクリプトを読み込む- PHP から、REST API の URL や翻訳済みの文言などの初期データを渡す
- 設定の読み書きは、権限確認付きの REST API ルートで行う
管理画面の機能はテーマを切り替えても残したいものなので、コードはテーマではなく独自プラグイン(または mu-plugins)に置きます。本番サイトにいきなり入れず、ステージング環境で動作を確かめてから反映してください。PHP のエラーで管理画面に入れなくなった場合でも、FTP などでプラグインのフォルダー名を変えれば無効化できます。
wp-content/plugins/my-vue-admin/
├── my-vue-admin.php
└── assets/
├── vue.global.prod.js
└── admin-app.js
管理ページを追加する
まず管理ページを追加します。表示用のコールバックでは、Vue のマウント先になる要素だけを出力します。ID は app のような一般的な名前を避け、プラグイン名の接頭辞を付けておくと他の機能とぶつかりません。
<?php
/**
* Plugin Name: My Vue Admin
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'admin_menu', function () {
add_menu_page(
'My Vue 設定',
'My Vue 設定',
'manage_options',
'my-vue-admin',
'my_vue_admin_render_page',
'dashicons-admin-generic'
);
} );
function my_vue_admin_render_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
echo '<div class="wrap"><h1>' . esc_html__( 'My Vue 設定', 'my-vue-admin' ) . '</h1>';
echo '<div id="my-vue-admin-app"></div></div>';
}
admin_enqueue_scripts でページを絞り込む
admin_enqueue_scripts のコールバックには、現在の管理ページを表す $hook_suffix が渡されます。add_menu_page() で追加したトップレベルのページなら toplevel_page_{メニュースラッグ}、add_submenu_page() で設定メニューの下に追加したページなら settings_page_{メニュースラッグ} のような値になります。この値と比べて、自分のページ以外では何もしないようにします。
add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
if ( 'toplevel_page_my-vue-admin' !== $hook_suffix ) {
return;
}
$base = plugin_dir_url( __FILE__ ) . 'assets/';
wp_enqueue_script(
'my-vue-admin-vue',
$base . 'vue.global.prod.js',
array(),
'3',
array( 'in_footer' => true )
);
wp_enqueue_script(
'my-vue-admin-app',
$base . 'admin-app.js',
array( 'my-vue-admin-vue', 'wp-api-fetch' ),
'1.0.0',
array( 'in_footer' => true )
);
wp_add_inline_script(
'my-vue-admin-app',
'window.myVueAdmin = ' . wp_json_encode( array(
'labels' => array(
'save' => __( '保存', 'my-vue-admin' ),
'saved' => __( '保存しました。', 'my-vue-admin' ),
'failed' => __( '保存に失敗しました。', 'my-vue-admin' ),
),
) ) . ';',
'before'
);
} );
自分のページの $hook_suffix が分からないときは、一時的に error_log( $hook_suffix ); を入れて debug.log で確認できます(確認後はこの行を削除します)。親メニューの位置やメニュー名の翻訳によって値が想定と異なる場合があるため、思い込みで書かずに実際の値を確かめてください。
wp_enqueue_script() の第5引数に配列を渡す書き方(in_footer や strategy)は WordPress 6.3 で入ったもので、現在はこちらが主流です(2026年10月現在)。古いバージョンでは真偽値の true を渡すとフッターで読み込まれます。
Vue 本体の置き方
WordPress のコアには Vue が含まれていないため、自分で用意します。ここでは npm でインストールした vue パッケージに含まれる dist/vue.global.prod.js をプラグイン内にコピーして使う前提にしています。
CDN から直接読み込む方法もありますが、管理画面のスクリプトが外部サーバーに依存すると、そのサーバーの障害や改ざんの影響を受けます。また WordPress.org のプラグインディレクトリで公開する場合は、外部から実行コードを読み込むことが原則として認められていないので、ガイドラインを確認してください。ファイルをプラグインに同梱しておけば、オフラインの開発環境でも動きます。
ビルドの種類にも注意が要ります。
- **
vue.global.prod.js(フルビルド)**:テンプレートを実行時にコンパイルする機能を含むので、下の例のようにtemplateに文字列を書く形で使える - **
vue.runtime.global.prod.js(ランタイムのみ)**:軽い代わりに、テンプレート文字列を書くと動かない。Vite などで単一ファイルコンポーネントを事前にビルドする場合向け
Vite でビルドした成果物が ES モジュール形式なら、type="module" で読み込む必要があります。WordPress 6.5 以降には wp_enqueue_script_module() という ES モジュール用の関数もありますが、従来の wp_enqueue_script() で読み込んだスクリプト(wp-api-fetch など)との依存関係の扱いが異なります。まずは IIFE 形式で出力して wp_enqueue_script() で読み込む方が、つまずきは少なくなります。
Vue アプリ側のコード
admin-app.js では、PHP から渡したデータを読み、wp.apiFetch で REST API を呼び出します。依存関係に wp-api-fetch を指定すると、管理画面ではログイン中ユーザーの REST 用 nonce を自動で付けてくれるため、nonce を手で渡す必要がありません。
( function () {
const { createApp, ref, onMounted } = Vue;
const config = window.myVueAdmin || { labels: {} };
createApp( {
setup() {
const message = ref( '' );
const saving = ref( false );
const notice = ref( '' );
onMounted( async () => {
const res = await wp.apiFetch( { path: '/my-vue-admin/v1/settings' } );
message.value = res.message;
} );
async function save() {
saving.value = true;
notice.value = '';
try {
const res = await wp.apiFetch( {
path: '/my-vue-admin/v1/settings',
method: 'POST',
data: { message: message.value },
} );
message.value = res.message;
notice.value = config.labels.saved;
} catch ( e ) {
notice.value = config.labels.failed;
} finally {
saving.value = false;
}
}
return { message, saving, notice, save, labels: config.labels };
},
template: `
<div>
<p><input type="text" class="regular-text" v-model="message"></p>
<p><button type="button" class="button button-primary" :disabled="saving" @click="save">{{ labels.save }}</button></p>
<p v-if="notice" role="status">{{ notice }}</p>
</div>
`,
} ).mount( '#my-vue-admin-app' );
} )();
Vue の {{ }} は値を自動でエスケープして表示します。一方 v-html はエスケープせずに HTML として挿入するので、ユーザーが入力した値や外部から取得した値には使わないでください。ボタンや入力欄には管理画面標準の button や regular-text クラスを付けておくと、追加の CSS なしで WordPress らしい見た目になります。
保存用の REST API ルート
設定の読み書きは、権限確認とサニタイズを入れた REST API ルートで受けます。permission_callback で manage_options を確認するので、管理者以外は読み書きできません。
add_action( 'rest_api_init', function () {
register_rest_route( 'my-vue-admin/v1', '/settings', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => function () {
return array( 'message' => (string) get_option( 'my_vue_admin_message', '' ) );
},
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => function ( WP_REST_Request $request ) {
update_option( 'my_vue_admin_message', $request->get_param( 'message' ) );
return array( 'message' => (string) get_option( 'my_vue_admin_message', '' ) );
},
'permission_callback' => function () {
return current_user_can( 'manage_options' );
},
'args' => array(
'message' => array(
'type' => 'string',
'required' => true,
'sanitize_callback' => 'sanitize_text_field',
),
),
),
) );
} );
args に sanitize_callback を書いておくと、コールバックに届く前に値が整えられます。画面側の Vue で入力チェックをしていても、REST API は直接呼び出せるため、サーバー側の検証は省略できません。
つまずきやすい点と確認方法
- **
Vue is not definedと表示される**:自作スクリプトの依存関係に Vue のハンドルが入っていないか、Vue のファイルの URL が間違っています。開発者ツールの「ネットワーク」でvue.global.prod.jsが 200 で読み込まれているか確認します。 - 画面に何も表示されない:マウント先の ID が PHP と JS で一致しているか、ランタイムのみのビルドでテンプレート文字列を使っていないかを確認します。コンソールの警告にヒントが出ることが多くあります。
- 他の管理ページの表示が崩れた:
$hook_suffixの条件が効いておらず、全ページで読み込まれている可能性があります。別の管理ページを開き、ネットワークに自作スクリプトが出ていないことを確かめます。 - 保存すると 401 や 403 になる:
wp-api-fetchを依存関係に入れているか、ログイン中のユーザーにmanage_optionsの権限があるかを確認します。 - 管理画面の CSS と見た目がぶつかる:自作の CSS はマウント先の ID を起点に書き、
buttonやinput全体を指定するような広いセレクターは避けます。
まとめ
- 管理画面に Vue を使うときは、
admin_enqueue_scriptsに渡される$hook_suffixで自分のページだけに読み込む - Vue 本体はプラグインに同梱し、テンプレート文字列を使うならフルビルド、事前ビルドならランタイムのみを選ぶ
- 初期データは
wp_add_inline_script()で渡し、REST API の呼び出しはwp-api-fetchに任せると nonce の扱いが簡単になる - 保存は
permission_callbackとsanitize_callbackを備えた REST API ルートで受け、表示は{{ }}に任せてv-htmlを避ける - 動かないときは、ネットワークでの読み込み状況、マウント先の ID、ビルドの種類、権限の順に確認する