タグアーカイブ JavaScript

Nuxt 4.6リリース、Server API刷新と高速化した型推論の全容

Nuxt 4.6リリース、Server API刷新と高速化した型推論の全容

Nuxt 4.6が2026年10月5日にリリースされた。Nuxt CLI v4が同梱され、開発サーバーの起動と再起動が大幅に速くなっている。今回の目玉はサーバーコードを移行しやすくする新しいimportソースだ。

サーバー非依存化への大きな一歩となる。型付き$fetchも再構築され、大規模プロジェクトで問題になっていた型推論の遅さが解消された。Nuxt 3は2026年7月末にEOLを迎えており、移行を考える契機になる。

Nuxt CLI v4の変更点

Nuxt CLI v4の変更点

Nuxt 4.6にはメジャーアップデート版のNuxt CLI v4が含まれる。依存関係として自動的に導入されるため、Nuxt 4.6に更新すれば新しいCLIが使える。変更の中心は開発サーバーだ。

インタラクティブパネルと新コマンド

端末の下部に専用パネルが固定表示される。URL、起動の進行状況、ショートカットキーが並ぶ。キー1つで再起動、ページを開く、ログ確認、リクエスト確認、ページとサーバールートの一覧表示が可能だ。

各リクエストにはIDが付与され、ログとエラーがどのリクエストから発生したか追跡できる。クラシックな出力に戻したい場合は--no-tuiを渡せばよい。

開発サーバーは再読み込みと再起動の理由も教えてくれる。どのnuxt.configキーが変わったのか、起動が遅い場合に時間がどこにかかったのか、各モジュールのセットアップ時間も個別に表示される。内容が変わらない保存では再起動が起きない。

数値で見る高速化

従来のCLI v3.37(Before)
インストールサイズ13.1 MB
依存関係70個
初回表示まで330 ms
メモリ使用量630 MB
↓
新しいCLI v4.0(After)
インストールサイズ3.5 MB
依存関係31個
初回表示まで50 ms
メモリ使用量440 MB

初回表示は6.6倍速くなり、メモリ使用量も30%減った。ロックファイルによって複数の開発サーバー起動が衝突せず、新しいnuxt curlやnuxt taskコマンドも動作する。

Server API nuxt/serverの導入

Server API nuxt/serverの導入

今回のリリースで最も重要な変更がサーバーコードの抽象化だ。新しいimportソースであるnuxt/serverが追加された。ハンドラー、ミドルウェア、ユーティリティをここから読み込む。ブラウザでも動くnuxt/appのサーバー版にあたる。

サーバー非依存化の目的

これまでサーバーコードはh3とNitroに密結合していた。h3やNitroのメジャーバージョンが上がると、エコシステム全体に影響が波及する構造だった。Nuxt 5でNitro v3とh3 v2へ移行するにあたり、この結合を解消する必要があった。

nuxt/serverは自前の型を定義し、外部パッケージを参照しない。同じハンドラーがNitro v2、Nitro v3、実験的なViteサーバーのどこでも動く。モジュール作者はh3やnitropackへのピア依存を気にせずに済む。

nuxt/serverの役割
アプリケーションコード import → nuxt/server
nuxt/server は裏で → Nitro(デフォルト)
nuxt/server は次期実装で → Viteサーバー(実験的)
■ 呼び出し元 ■ 共通API ■ 実装バックエンド

サーバーコードをnuxt/serverで書いておけば、Nuxt 5に更新しても同じファイルがそのまま動く。モジュールも1つのファイルで両バージョンをサポートできる。

コード例とAPIサーフェス

import { defineEventHandler, getQuery } from 'nuxt/server'

export default defineEventHandler((event) => {
  const { name } = getQuery<{ name?: string }>(event)
  return { message: `Hello, ${name ?? 'world'}!` }
})

イベントオブジェクトはWeb標準に基づく。リクエストURL、レスポンスヘッダー、クッキー、リダイレクト、セッションなどが扱える。必要ならZodなどのバリデーションライブラリと組み合わせて、リクエスト本文を検証してから処理を進められる。

従来のh3やnitropackから読み込む方法も削除されていない。特定のハンドラーで低レベルな機能が必要な場合は、これまで通りdefineEventHandlerをh3から読み込めば動く。

Typed $fetchの再構築

Typed $fetchの再構築

$fetchとuseFetchはサーバールートから型を推論できた。しかし数百ルートを超えるとTypeScriptのインスタンス化上限に達し、コンパイルが止まることがあった。

型推論の高速化

型システムをfetchdtsベースで再実装した。静的なパスは完全一致テーブルとしてコンパイルされ、ルート数ではなく参照箇所の数で処理コストが決まる。

従来の型推論(Before)
100ルート0.77秒
300ルート3.49秒(エラー)
1000ルート7.44秒(エラー)
3000ルート53.63秒(エラー)
↓
再構築後の型推論(After)
100ルート0.26秒
300ルート0.16秒
1000ルート0.20秒
3000ルート0.62秒

3000ルートの処理が53.63秒から0.62秒に短縮された。ピークメモリも946 MBから140 MBへ大幅に減っている。大規模プロジェクトほど効果を実感できる。

新しい型チェック機能

ルートごとに検証済みのbody、query、headersが型情報に含まれる。間違った形のリクエストを送ると、呼び出し側でエラーが出るようになった。存在しないパスへの$fetchを拒否するexperimental.strictRouteTypesも追加されている。

Nuxt 4ではオプトイン機能だ。Dateの戻り値が呼び出し側ではstringになるなどの小さな推論の違いがあるため、デフォルトにはしていない。Nuxt 5では標準で有効になる。

開発体験の改善

開発体験の改善

開発時のエラー表示と起動画面が刷新された。開発者にとって地味に見える変更だが、原因特定までの時間を短縮する効果は大きい。

my-badによる新しいエラー表示

サーバーサイドエラーのスタックトレースにソース位置とコードフレームが表示されるようになった。Youchの埋め込みiframeは廃止され、my-badという新しいオーバーレイに置き換えられた。

エラーレポートにはコピーボタンがある。Markdown形式、エージェントに手渡せるプロンプト形式、JSON形式を選べる。AIコーディングエージェントが普及した今、エラー情報をそのまま渡せる設計は実務的だ。

致命的な起動エラーもページとして表示される。nuxt.config.tsに構文エラーがあっても専用ページが表示され、修正後に自動で再読み込みされる。ワーカー再起動をまたいでエラー情報が保持される仕組みだ。

Vue Vapor対応とローディング画面

Vue 3.6のVapor Modeに相互運用モードで対応した。アプリ本体は仮想DOMのまま、コンポーネント単位でVaporを選択できる。<script setup vapor>と書くだけだ。ルーティングやuseAsyncData、レイアウトは変更なしで動く。

開発サーバー起動中のローディング画面も新しくなった。WebGL2のパーティクルフィールドが山脈を描く。WebGL2非対応環境では静止画にフォールバックし、prefers-reduced-motion設定時はアニメーションを止める。モーションに配慮した実装になっている。

パフォーマンスと移行

パフォーマンスと移行

実測値と改善ポイント

ベンチマークではNuxt 4.5.2と比較して、スタータープロジェクトのビルドが4.4秒から3.6秒に短縮された。200ページ・200コンポーネント・50ルートのビルドも11.6秒から10.3秒に改善している。

SSRスループットは300個の<NuxtLink>を含むページで1秒あたり110リクエストから140リクエストに向上した。内部リンクがサーバー上でプレーンな<a>タグとしてレンダリングされるようになり、リアクティブ状態を持たなくなった効果だ。

experimental.early404を有効にすると、ルートに一致しないリクエストがVueアプリの生成をスキップする。30ページでブート処理が約23 msのアプリでは、JSON 404応答が37.1 msから0.3 msに短縮される。

Nuxt 5への準備

Nuxt 5のデフォルト機能の多くがすでに4.6に入っている。future.compatibilityVersion: 5を設定すれば、一括で有効にできる。個別にトグルすることも可能だ。

  • 型付きページ(experimental.typedPages)
  • 型付き$fetch(experimental.routeTypedFetch)
  • 大文字小文字を区別するルーティング
  • ビルド時のページメタデータ抽出
  • インラインエラーレンダリング

サーバーコードをnuxt/serverで書いておけば、Nuxt 5移行時に書き換えが不要になる。今のうちに移行しておくのが安全だ。

アップグレードはnpx nuxt upgrade --dedupeを実行する。ロックファイルを更新し、Nuxt CLI v4を含む最新の依存関係が導入される。Node.jsは22.22.3以上、24.15.0以上、または26.0.0以上が必要だ。

この記事のポイント

  • Nuxt 4.6はサーバー非依存化への大きな一歩。新しいimportソースnuxt/serverが追加された
  • Nuxt CLI v4が同梱され、開発サーバーの初回表示が330 msから50 msへ高速化
  • 型付き$fetchが再構築され、3000ルートの型推論が53.63秒から0.62秒に短縮
  • Nuxt 3は2026年7月末にEOL。移行を進めるならfuture.compatibilityVersion: 5で事前テストが可能
  • アップグレードはnpx nuxt upgrade --dedupeで実行できる
WordPress管理画面でトグル設定が勝手にオンに戻る時の原因と対処法

WordPress管理画面でトグル設定が勝手にオンに戻る時の原因と対処法

WordPress管理画面のトグル設定をオフにしても勝手にオンに戻る場合、原因は主にJavaScriptエラーかキャッシュの不整合にある。トラッキング不具合時の診断メール設定が保存されない症状では、画面の表示とデータベースの値がずれている可能性が高く、切り分け手順を順に試すことが最も確実な対処法だ。

トグル設定が勝手にオンへ戻るのはなぜか

トグル設定が勝手にオンへ戻るのはなぜか

WordPressの管理画面にあるトグルボタンは、押した瞬間にAJAX通信でサーバーへ設定値を送信する仕組みだ。送信された値はnonce検証を経てデータベースに保存される。ここで問題が起きると、画面上ではオフに切り替わったように見えても、データベースには保存されないままになる。

別のページへ移動して戻った時、画面はデータベースから最新の設定値を読み込む。保存が失敗していれば、以前の値がそのまま表示されるためトグルがオンへ戻ったように見える。これが今回の症状の正体だ。

特に「トラッキング不具合時の診断メール」のような通知系の設定は、プラグイン側がデフォルトでオンにする仕様になっている場合がある。保存の失敗とデフォルト値の再適用が重なると、オフにしても戻るという挙動が顕著に現れる。

原因になりやすい要素は以下のとおりだ。

  • 管理画面でJavaScriptエラーが発生しAJAX送信が失敗している
  • キャッシュプラグインやオブジェクトキャッシュが古い設定値を返している
  • テーマや別のプラグインが設定を強制的にオンへ戻している
  • セキュリティプラグインがnonce検証やAJAX通信をブロックしている
  • データベースへの書き込み権限やオプション保存処理に問題がある
トグル設定の保存フローと失敗ポイント
オフに操作 → AJAXリクエスト送信 → データベース更新 → 保存完了
↓
AJAXエラー発生 → 保存に失敗 → 次回表示時にオンへ戻る
■ 正常時 ■ エラー時

このデモはトグル設定の保存が正常に終わる場合と、途中で失敗して次回表示時に設定が戻ってしまう場合のフローを示している。次節からは原因を特定するための手順を具体的に進めていく。

JavaScriptエラーをブラウザで確認する手順

JavaScriptエラーをブラウザで確認する手順

最初に確認するべきは管理画面でJavaScriptエラーが発生していないかだ。ブラウザの開発者ツールを使えば、AJAX通信が失敗した原因を直接確認できる。Chromeの場合はF12キー、Macの場合はCommand+Option+I、Windowsの場合はCtrl+Shift+Iで開発者ツールが開く。

開発者ツールが開いたらConsoleタブに切り替える。ここに赤いエラーメッセージが表示されていれば、それがトグル設定の保存を妨げている原因になる。特に「FAILED」「POST」「403」「500」のような表示がある場合は、AJAX通信そのものが失敗している証拠だ。

エラーが確認できた場合は、エラーメッセージの内容を控えておく。ファイル名や関数名が表示されていれば、どのプラグインやテーマがエラーの原因かを特定する手がかりになる。調査を次の段階へ進める前に、まずはエラーの有無を確認するだけで状況が大きく変わる。

JavaScriptエラーを確認する流れ
STEP 1 管理画面を開いた状態で開発者ツールを起動する
↓
STEP 2 Consoleタブで赤いエラーメッセージの有無を確認する
↓
STEP 3 エラーがあれば内容を控え、発生源を特定する
↓
STEP 4 トグル操作を再現しエラーが増えるか確認する

このデモはJavaScriptエラーの確認から発生源の特定までを順に示している。トグル操作を再現してエラーが増える場合は、そのトグルに関連するスクリプトでエラーが起きている可能性が高い。

キャッシュを削除して保存状態を確認する方法

キャッシュを削除して保存状態を確認する方法

JavaScriptエラーが見つからない場合はキャッシュの影響を疑う。サイト全体のキャッシュを削除してからトグル設定を再度オフにし、別ページへ移動して戻るという流れで確認する。WordPressでは複数のキャッシュ層が重なっていることがあり、それぞれを順にクリアする必要がある。

キャッシュプラグインのキャッシュを削除する

キャッシュプラグインを利用している場合は、プラグインの管理画面からキャッシュを全削除する。代表的なプラグインでは「キャッシュを消去」「Purge All」のようなボタンが用意されている。削除後、トグル設定をオフにして別ページへ移動し、戻って状態を確認する。

オブジェクトキャッシュを無効化して確認する

高速化のためにRedisやMemcachedといったオブジェクトキャッシュを導入している場合は、一時的に無効化して確認する。オブジェクトキャッシュはデータベースの値をメモリ上に保持するため、更新処理が古い値を返し続けてしまうことがある。wp-config.phpに記載されているキャッシュ関連の設定を一時的に外すか、サーバー側のキャッシュサービスを停止してテストする。

ブラウザキャッシュを削除する

ブラウザ側のキャッシュが管理画面の表示に影響することは少ないが、シークレットウィンドウを使えば完全にキャッシュの影響を排除できる。シークレットウィンドウで管理画面へログインし、トグル設定をオフに変更してから別ページへ移動し、再度戻って状態を確認する。この方法で問題が再現しなければ、通常のブラウザセッションにキャッシュが残っていたことになる。

プラグイン競合を切り分ける手順

プラグイン競合を切り分ける手順

JavaScriptエラーやキャッシュの影響が見つからない場合、次に疑うのはプラグイン同士の競合だ。特にセキュリティ系プラグインや高速化系プラグインは、管理画面のAJAX通信に干渉することがある。テーマに含まれるスクリプトが原因になることも少なくない。

切り分けは次の順序で行う。まず全プラグインを一括で無効化し、標準テーマ(Twenty Twenty-FourやTwenty Twenty-Fiveなど)へ切り替える。この状態でトグル設定をオフに変更し、別ページへ移動して戻る操作を行い、設定が保持されるか確認する。

この状態で問題が再現しなければ、プラグインの1つずつを有効化して同様の操作を繰り返す。問題が再現した時点で、直前に有効化したプラグインが原因の可能性が高い。特定できたらそのプラグインの設定を見直すか、開発元へ情報を集める。

なお全プラグインの一括無効化はサイトの表示にも影響を与える。切り分け作業を行う場合は、アクセスの少ない時間帯を選ぶかステージング環境を利用する。

プラグイン競合の切り分け手順
STEP 1 全プラグインを無効化し標準テーマへ切り替える
↓
STEP 2 トグルをオフにし別ページへ移動して戻る
↓
STEP 3 問題がなければプラグインを1つずつ再有効化する
↓
STEP 4 問題が再現した時点で原因プラグインを特定する

このデモはプラグイン競合の特定を効率的に進めるための順序を示している。問題が再現したタイミングを記録しておくと、原因プラグインの特定がスムーズになる。

データベースの設定値を直接確認して修正する方法

データベースの設定値を直接確認して修正する方法

ここまでの切り分けで原因が特定できない場合は、データベースに保存されている設定値を直接確認する。WP-CLI(WordPress Command Line Interface。WordPressのコマンドライン操作ツール)が使える環境であれば、管理画面を経由せずにオプション値を調べられる。

WP-CLIでオプション値を検索する

トグル設定に関連するオプション名を確認するには、wp option listコマンドを使う。オプション名はプラグインごとに異なるが、diagnosticやtrackingといったキーワードを含むものが対象になる。該当するオプションの値がオン(1やyesと表示される)になっていれば、設定がデータベースに保存されていないことが確定する。

wp option list | grep -i diagnostic
wp option list | grep -i tracking

見つかったオプション名を指定して値を確認するには、wp option getコマンドを使う。値がオンを示していれば、トグル設定が保存されていないことになる。値がオフを示していれば、保存自体は成功しているため別の要因が影響している。

wp option get 該当するオプション名

phpMyAdminで直接確認する場合

WP-CLIが使えない環境ではphpMyAdminを使う。データベース内のwp_optionsテーブル(プレフィックスが異なる場合は該当するテーブル)から、option_name列にキーワードを含む行を検索する。option_value列の値がオンを示していれば保存が失敗している。

検索にはSQL文を使う。プレフィックスは環境によって異なるため、テーブル名を確認してから実行する。

SELECT * FROM wp_options WHERE option_name LIKE '%diagnostic%';

直接データベースを変更する場合は必ずバックアップを取ってから行う。設定値がシリアル化された配列の中に含まれている場合、直接変更するとデータ構造が崩れるリスクがある。不安がある場合は手動更新を避け、プラグインの設定画面から保存し直す方が安全だ。

確認結果による原因の判定
値がオン 保存が失敗しているためJavaScriptエラーやAJAX通信の問題を再調査する
↓
値がオフ 保存は成功しているためフィルターやキャッシュの影響を調査する

このデモはデータベースの設定値の状態から原因の方向性を切り分ける方法を示している。保存の成否を知ることで、その後の調査対象を絞れる。

よくある質問

トグル設定が勝手に戻るのはいつどんな状況で起きるのか

主にJavaScriptエラーが発生している場合に起きやすい。管理画面のトグルはAJAX通信で設定を保存するが、他のプラグインやテーマが読み込むスクリプトと競合して通信が失敗すると、画面表示だけが切り替わって保存が行われない。キャッシュプラグインの影響で古い設定値が表示される場合もある。

キャッシュプラグインを使っていないのに設定が戻るのはなぜか

サーバー側のオブジェクトキャッシュやホスティング会社が提供する静的キャッシュが影響している可能性がある。またテーマのfunctions.phpや別のプラグインに、設定を強制的にオンへ戻すコードが含まれている場合もある。データベースの値を直接確認すれば保存の成否がわかる。

セキュリティプラグインが原因になることはあるか

ある。セキュリティプラグインが管理画面のAJAXリクエストやnonce検証をブロックすると、設定の保存が失敗する。ファイアウォール機能が過剰に反応して管理画面の操作を妨げるケースがあるため、一時的に無効化して切り分けを行う必要がある。

設定値をデータベースから直接変更しても問題ないか

問題ない場合が多いが、バックアップを取ってから行うことが必須。WP-CLIでオプション名を確認してからupdate_optionを行う。ただしプラグインの設定構造によってはシリアル化された配列に含まれており、直接変更が難しいこともある。不安がある場合はプラグインの設定画面から保存し直す方が安全だ。

本体のアップデートで解消されることがあるか

ある。プラグイン側のバグが原因の場合は本体アップデートで修正されることが多い。プラグインとWordPress本体を最新版に更新してから再度確認する価値は十分にある。ただし更新前にバックアップを取っておくと安心だ。

この記事のポイント

  • トグル設定が勝手に戻る原因の多くはJavaScriptエラーかキャッシュの不整合
  • ブラウザの開発者ツールでconsoleエラーを確認するのが最初の切り分け手順
  • キャッシュプラグインとオブジェクトキャッシュを順に無効化して特定する
  • プラグインを全停止し1つずつ再有効化して競合元を見つける
  • 最終手段としてWP-CLIやphpMyAdminからデータベースの設定値を直接確認する
Bun v1.4.1リリース。アイドル時メモリ削減とバンドルサイズ最大85%減の全容

Bun v1.4.1リリース。アイドル時メモリ削減とバンドルサイズ最大85%減の全容

Bun v1.4.1が2026年9月4日にリリースされた。202件の問題を修正し、236件のリアクションに対応したメンテナンスリリースだ。アイドル時のメモリ削減、HTTP/2対応、バンドルサイズの大幅な最適化が含まれている。

特にNext.js SSRのアイドル時RSSは222MBから142MBへ削減された。bun buildではzod 4.5のバンドルサイズが375.3KBから77.3KBへ79%減っている。長期稼働するサーバーや大きなモノレポを扱う開発者にとって、実務に直結する変更だ。

この記事では、実務に影響する主要な変更点をランタイム、bun install、bun build、bun testの順に解説する。

Bun v1.4.1の概要と導入

Bun v1.4.1の概要と導入

BunはNode.js互換のJavaScriptランタイムであり、パッケージマネージャやバンドラー、テストランナーも備える。v1.4.1ではアイドル時のメモリ使用量の削減、Bun.serveのHTTP/2対応、bun buildのバンドル最適化が目玉だ。

インストールとアップグレード

Bunのインストール方法は複数用意されている。macOSやLinuxではcurlコマンド、npmを使う方法、Windowsではpowershellやscoop、macOSではbrew、Dockerイメージも提供される。既存環境のアップグレードは bun upgrade で完了する。

curl -fsSL https://bun.sh/install | bash
npm install -g bun
bun upgrade

このリリースの位置づけ

v1.4.1は202件の問題を修正した。前バージョンv1.4.0で入った回帰の修正も多い。新機能として、ランタイムのメモリ削減、bun installのオフライン対応、bun buildのコード分割改善、bun testの分離実行の修正が盛り込まれた。

ランタイムのメモリ削減と高速化

ランタイムのメモリ削減と高速化

アイドル時のメモリ使用量を大幅削減

BunのJavaScriptエンジンであるJavaScriptCoreが、長時間のアイドル期間後にJIT生成コードを破棄するようになった。これにより、長期稼働するプロセスのメモリ使用量が大きく減る。負荷を60秒かけた後、3分間アイドル状態にしたLinux x64環境のRSSは、Next.js SSRで142MB、vite devで111MB、Expressで53MBだった。前バージョンではそれぞれ222MB、142MB、65MBである。

従来のBun v1.4.0(Before)
Next.js SSR 222 MB
vite dev 142 MB
Express 65 MB
↓
Bun v1.4.1(After)
Next.js SSR 142 MB
vite dev 111 MB
Express 53 MB

上記は60秒の負荷後、3分間アイドル状態にしたときのRSSをLinux x64で比較した結果だ。アイドル時にJITコードを消す仕組みにより、常駐プロセスのメモリコストが抑えられている。

AsyncLocalStorageとBufferの高速化

AsyncLocalStorage.run() が約2倍高速になった。アクティブなストアがあるとき、await や .then()、.finally() のたびに余分なメモリ確保が発生しなくなったためだ。Node.js 26との比較では、als.run() が15.9ns/op、Node.jsの410ns/opに対して大幅に速い。

Bufferの読み書きも高速化された。writeFloatLE() は2.85nsから0.31nsへ9.2倍、writeUInt8() は2.24nsから0.31nsへ7.2倍、readUInt32BE() は0.76nsから0.42nsへ1.8倍速くなった。macOS arm64の計測だ。

モジュール読み込みと表示処理の高速化

組込みNode.jsモジュールの require() が遅延初期化になった。node:assert は6.22msから0.64msへ、node:fs は2.75msから0.87msへ短縮されている。テストやCLIツールの起動時間に効く変更だ。

オブジェクト表示も高速化した。1万6,000個のキーを持つオブジェクトの Bun.inspect() は140msから3.2msへ43倍速くなった。テキストの色付けや console.log()、util.inspect() にも同じコードが使われる。

HTTPSの初回接続も最大3倍速くなった。ルート証明書をDER形式で埋め込み、必要になった時だけ解析する方式へ変更したためだ。--use-system-ca を使う場合は46.3msから15.3msへ短縮された。

Bun.serveとWeb APIの強化

Bun.serveとWeb APIの強化

HTTP/2対応とTLS検証の変更

Bun.serve() がHTTP/2を同一ポートでサポートした。TLS接続ではALPNでプロトコルを自動ネゴシエーションし、平文接続ではHTTP/2 prefaceを送ってきたクライアントにHTTP/2で応答する。http1 オプションをfalseにすればHTTP/1.xクライアントを拒否できる。WebSocketとレスポンストレーラーはHTTP/2では未対応だ。

セキュリティ面では、fetch() のTLS検証がURLのホスト名を基準に変更された。従来はカスタム Host ヘッダーをTLSサーバー名として使っていた。プロキシなどでユーザー入力のHostヘッダーを渡す場合があるため、この変更は重要だ。IPアドレスに接続して別名で証明書を検証したい場合は tls.servername を指定する。

WebSocketのpauseとresume

BunのWebSocketクライアントに pause() と resume() が追加された。メッセージの処理速度が受信速度に追いつかないとき、TCPソケットからの読み取りを停止できる。停止中はメモリにメッセージが溜まらず、送信側にはTCPバックプレッシャーが伝わる。

const socket = new WebSocket("wss://example.com/feed");

socket.addEventListener("message", (event) => {
  if (!file.write(event.data)) {
    socket.pause();
    file.once("drain", () => socket.resume());
  }
});

これはBun独自の拡張であり、ブラウザでは利用できない。pause中のソケットは socket.isPaused で状態を確認できる。bufferedAmount も送信待ちバイト数を正しく報告するようになった。

Bun.writeのストリーミング書き込み

Bun.write() が Response や ReadableStream の本文を、いったんメモリに読み込まずにファイルへストリーミングするようになった。128MiBのダウンロードを書き込むケースでは、ピークRSSの増加が161MBから13MBへ減った。

bun installのオフライン対応とワークスペース改善

bun installのオフライン対応とワークスペース改善

–offlineと–prefer-offline

bun install --offline はネットワーク要求を一切行わない。すべてのパッケージがキャッシュに存在する必要があり、CIでキャッシュを復元するケースやネットワークに接続できないマシン向けだ。キャッシュに無いパッケージがあると、その名前を明示したエラーになる。

bun install --prefer-offline はキャッシュを優先する。期限切れでもキャッシュのコピーを使い、キャッシュに無いパッケージだけダウンロードする。どちらも bunfig.toml に設定すればデフォルトにできる。

通常の bun install
キャッシュの有効期限が切れると、新しいバージョンを確認するためメタデータを再取得する
bun install –prefer-offline
期限切れでもキャッシュを優先する。キャッシュにないパッケージだけダウンロードする
bun install –offline
ネットワーク要求を一切行わない。キャッシュに無いパッケージがあればエラーになる

self-contained node_modules

ワークスペースのpackage.jsonに "selfContained" を指定すると、そのパッケージ専用の node_modules が作られる。Electronのように特定の node_modules レイアウトを期待するツール向けだ。Yarnの hoistingLimits 設定も利用できる。

bun buildのバンドル最適化とコンパイル改善

bun buildのバンドル最適化とコンパイル改善

export * asによるバンドルサイズ削減

zodやEffectのように export * as でエクスポートをまとめるライブラリで、未使用エクスポートのツリーシェイクが効くようになった。v1.4.0では z.object() を呼んだ場合でもグループの全エクスポートを保持していた。v1.4.1では直接参照にコンパイルされ、不要なエクスポートが削除される。

Bun v1.4.0(Before)
zod 4.5 375.3 KB
fp-ts 2.16 21.8 KB
effect 3.22 369.1 KB
↓
Bun v1.4.1(After)
zod 4.5 77.3 KB (79%削減)
fp-ts 2.16 3.2 KB (85%削減)
effect 3.22 163.6 KB (56%削減)

各ライブラリから2〜3個の関数だけを呼ぶ小規模なプログラムを、bun build --minify でビルドした結果だ。zodでは252個のエントリを持つ名前空間オブジェクトが生成されていたが、v1.4.1ではすべて削除された。

動的importのtree-shakingとコード分割

import() で読み込むモジュールでも、未使用のエクスポートが削除されるようになった。const { z } = await import("zod") は静的な import { z } from "zod" と同じコードにバンドルされる。zod 4.5では377.9KBから78.2KBへ縮む。

--splitting も改善された。40個の遅延ルートを持つテストアプリでは、出力ファイルが219個から151個に、出力サイズが124KBから75KBに減った。さらに --min-chunk-size オプションで小さなチャンクを統合できる。ブラウザ向けビルドでは link rel="modulepreload" が自動追加され、チャンクのフェッチが並列化される。

–compileの起動高速化とバイトコード削減

コンパイル済み実行ファイルの起動も速くなった。BunでコンパイルされたClaude Codeでは、入力ボックスが表示されるまでの時間が397msから318msへ20%短縮された。バイトコードのサイズも最適化され、これまで元ソースの約9倍だったものが約3倍になった。Claude Codeのインストールサイズは376MBから207MBへ45%減っている。

--bytecode-depth で事前コンパイルする関数のネスト深度を制限できる。--bytecode を付けたクロスコンパイルもmacOSやLinuxからWindows x64へ対応した。テキストインポートは1回だけ埋め込まれ、実行時の解析とコピーが不要になった。

この記事のポイント

  • Bun v1.4.1は202件の問題を修正し、アイドル時のメモリ使用量を大幅に削減した
  • Bun.serveがHTTP/2に対応し、WebSocketにpauseとresumeが追加された
  • bun installにオフラインモードが加わり、CIやネットワーク遮断環境で使いやすくなった
  • bun buildはzodやEffectのバンドルサイズを最大85%削減し、コード分割も改善した
  • bun build –compileは起動時間とバイトコードサイズを最適化した
WooCommerceチェックアウトでPayPalボタンが表示されない原因と修正手順

WooCommerceチェックアウトでPayPalボタンが表示されない原因と修正手順

独自テーマを使った WooCommerce サイトのチェックアウトページで PayPal ボタンが表示されない場合、主な原因は DOM の準備完了前に async で読み込まれた JavaScript がコンテナ要素を取得できずに失敗することだ。また、PayPal JS SDK の読み込み完了後に buttons メソッドが存在しない問題は、コンストラクタの引数順序の誤りやスクリプトの初期化タイミングの競合で発生する。これらの問題を順に切り分けて修正すれば、数分でボタンが復活する。

PayPalボタンが表示されない原因を特定する

PayPalボタンが表示されない原因を特定する

まずコンソールに出力されているエラーを把握する。大半のケースで、Error: Document is ready and element #paypal-button-container does not exist(日本語環境では同様のエラーメッセージが英語で表示される)という致命的なエラーが記録されている。これは PayPal JS SDK が #paypal-button-container という要素を DOM から見つけられず、ボタンの描画を中止したことを意味する。同時に、スクリプトがチェックアウトページではなく商品カテゴリページで動作してしまう現象や、CORS(クロスオリジンリクエスト)がブロックされたというエラーも散見される。

以下のフローで問題を切り分けると、原因に早くたどり着ける。

STEP 1 チェックアウトページのソースを開き #paypal-button-container が実在するか確認する
↓
STEP 2 PayPalManager が読み込まれているか console.log でオブジェクトを確認する
↓
STEP 3 init() 後の this.paypal.buttons が undefined でないか検証する
↓
STEP 4 スクリプトがチェックアウトページ以外で実行されていないか調べる

この4つのチェックポイントをもとに、次のセクションで具体的な修正を加えていく。

async読み込みによるDOM参照の競合を解決する

async読み込みによるDOM参照の競合を解決する

WordPress 7.0 では、wp_enqueue_script に 'strategy' => 'async' を指定すると、スクリプトが非同期で読み込まれる。この設定自体は高速化に有効だが、DOM の構築が完了する前に document.querySelector('#paypal-button-container') が実行されると、要素がまだ存在しないために null が返ってしまう。

解決策はシンプルだ。PayPal ボタンの初期化処理全体を DOMContentLoaded イベントの中に包み、DOM の準備完了を待つ。具体的には index.js の setupPayment() を次のように修正する。

document.addEventListener('DOMContentLoaded', async function() {
    const paypalManager = new PayPalManager(
        clientIdHere,
        '.checkoutForm',
        '#paypal-button-container',
        orderEndPoint,
        emailEndPoint
    );
    await paypalManager.init();
    await paypalManager.renderButtons(paypalManager.buttonContainer);
});

こうすれば、DOMContentLoaded が発火した時点でコンテナ要素が確実に存在するため、null エラーが解消する。なお、async ストラテジーはそのままでも問題ないが、より確実な制御を求めるなら defer に切り替えてもよい。ただし defer も DOM 構築の完了前に実行される可能性があるため、イベントリスナーを組み合わせるのが最も安全だ。

Before(エラー)
async 読み込み → 即座に querySelector が走り、container は null
After(修正)
DOMContentLoaded の後で初期化 → container を確実に取得
■ エラー状態 ■ 修正後

コンストラクタの引数順序と初期化タイミングを修正する

コンストラクタの引数順序と初期化タイミングを修正する

コンソールに出力されたオブジェクトを見ると、buttonContainer が null、orderEndPoint と emailEndPoint の値が意図したものと逆になっているケースが多い。これは PayPalManager のコンストラクタを呼び出す際の引数の順序がずれているか、引数が不足しているときに起こる。

典型的なミスは、最初の引数(clientId)を空にしてしまったり、セレクタ文字列を間違った順番で渡してしまうことだ。次のように constructor と new の呼び出し側を一致させる必要がある。

// paypal_manager.js の constructor 定義(本来の正しい順序の例)
constructor(clientId, formSelector, containerSelector, orderEndPoint, emailEndPoint) {
    this.clientId = clientId;
    this.formSelector = formSelector;
    this.containerSelector = containerSelector;
    this.buttonContainer = null;
    this.orderEndPoint = orderEndPoint;
    this.emailEndPoint = emailEndPoint;
    this.paypal = null;
}
// index.js でのインスタンス化(すべての引数を順序通りに正しく与える)
const paypalManager = new PayPalManager(
    'your-client-id',          // clientId
    '.checkoutForm',           // formSelector
    '#paypal-button-container',// containerSelector
    'https://api-m.sandbox.paypal.com/v2/checkout/orders', // orderEndPoint
    '/wp-json/auto-parts/v3/send_order_email'               // emailEndPoint
);

このコードのように引数を明示的に記述すれば、プロパティの食い違いが一掃される。また、init() 内で this.paypal.buttons が undefined になる問題は、loadScript() の完了を await で待つ処理が正しく記述されていれば解決する。paypal-js パッケージの loadScript は Promise を返すので、必ず await loadScript(...) の形で使う。

チェックアウトページにだけスクリプトを読み込む方法

チェックアウトページにだけスクリプトを読み込む方法

症状のひとつに「スクリプトが商品カテゴリページでは動くのにチェックアウトページで動かない」という現象があった。これは wp_enqueue_scripts フックがすべてのページで実行されるために、意図しないページで PayPal のコードが動き出し、逆にチェックアウトページでは何らかの理由でコンテナが存在したにもかかわらず失敗していることを示す。

第一に、スクリプトの読み込みをチェックアウトページに限定する。WordPress の条件分岐タグ is_checkout() を使い、functions.php を次のように変更する。

function mainModules() {
    if ( is_checkout() && ! is_order_received_page() ) {
        wp_enqueue_script(
            'paypal-checkout',
            get_theme_file_uri('/build/index.js'),
            array(),
            '1.0.0',
            array( 'strategy' => 'async' )
        );
    }
}
add_action('wp_enqueue_scripts', 'mainModules');

この条件を追加すれば、商品一覧やカテゴリページで PayPal 関連の JavaScript エラーがコンソールに表示されなくなり、意図したページだけが初期化を行う。また、キャッシュプラグインが原因で古いスクリプトが配信されている可能性があるため、修正後にキャッシュをクリアすることも忘れずに行う。

CORSエラーの対処と無視できる場合

CORSエラーの対処と無視できる場合

コンソールに記録される Cross-Origin Request Blocked エラーは、PayPal のロガー API(https://www.sandbox.paypal.com/xoplatform/logger/api/logger)へ送信されるリクエストがオリジン間制限に引っかかったものだ。このエラーは、PayPal ボタンの描画が失敗した後に二次的に発生するケースがほとんどで、サイトの主要機能には影響しない。

ボタンが正しく表示されるようになれば、多くの場合この CORS エラーは自然に消える。もし修正後も CORS エラーが出続けるなら、PayPal の Sandbox アプリケーション設定で許可されたオリジンに本番ドメインが登録されているか確認する。通常は無視して問題ないが、セキュリティ上の懸念がある場合は、PayPal のビジネスサポートにオリジン制限の解除を依頼できる。

よくある質問

asyncとdeferのどちらを選ぶべきか

両者とも非同期読み込みだが、defer は HTML のパース完了後に実行順序を保ちながら実行される。PayPal ボタンのような DOM 操作が絡むスクリプトは、defer と DOMContentLoaded の組み合わせが扱いやすい。ただし、パフォーマンスを優先するなら async のままイベントリスナーで制御する形で問題ない。

PayPal SDK の読み込みに時間がかかる場合の改善策

@paypal/paypal-js の loadScript は PayPal の CDN からスクリプトを取得する。自前でキャッシュすることは難しいが、async 読み込みによってページ全体のレンダリングをブロックしないようにできる。どうしても速度が遅い場合は、チェックアウトページ遷移時にローディングスピナーを表示し、init() が完了するまでユーザーに待機を伝える UI を実装するとよい。

コンソールのCORSエラーを完全に消す方法はあるか

PayPal 側のロガーAPIのオリジンを制御することはできないため、完全に消すことは難しい。実害がないため無視するのが一般的だ。もしどうしても気になる場合は、エラーハンドリングで該当のリクエストをキャッチして無視するか、PayPal のサポートに問い合わせてロガー機能を無効化できないか相談する。

WordPressのバージョンが7.0でなくてもこの問題は起きるか

async ストラテジーは WordPress 6.6 以降で導入されたため、それ以前のバージョンでは異なる方法で非同期読み込みを行っている可能性がある。しかし、DOM の準備完了前に要素を取得できない問題は、wp_enqueue_script の設定にかかわらず生じるため、同じ修正が有効だ。

子テーマで上書きする場合の注意点

親テーマの functions.php で定義された wp_enqueue_scripts フックを子テーマで解除するには、remove_action を使うか、子テーマ側のフックで wp_dequeue_script を使って親のスクリプトを外した後、新たに読み込む。ペイパルマネージャーの JavaScript ファイルは通常、テーマのビルドフォルダにあるため、ファイル自体を子テーマにコピーして上書きする方法が確実だ。

この記事のポイント

  • PayPalボタンが表示されない根本原因は、DOMの準備完了前にスクリプトがコンテナ要素を取得できないこと
  • async読み込みの対策として、DOMContentLoadedイベントの内側で初期化を行う
  • コンストラクタ引数の順序ずれで、buttonContainerがnullになったりエンドポイントが入れ替わる
  • is_checkout() 条件を使ってチェックアウトページにだけスクリプトを読み込む
  • 二次的に発生するCORSエラーはボタン描画が成功すれば消えることが多く、実害がなければ無視できる
WPC Linked Variation 更新で管理画面の依存スクリプトエラーを修正する方法

WPC Linked Variation 更新で管理画面の依存スクリプトエラーを修正する方法

WooCommerceの商品バリエーションを扱うプラグイン「WPC Linked Variation」(WooCommerce用)を使っていると、管理画面のあらゆるページで「依存関係にあるスクリプトが登録されていません」という警告が表示される場合がある。これはバージョン4.4.3以前の不具合であり、最新版4.4.4以降へアップデートすればすぐに消える。

エラー「正しく呼び出されていません」の中身と発生原因

エラー「正しく呼び出されていません」の中身と発生原因
エラー状態
管理画面の任意のページで、「依存関係 wc-enhanced-select, selectWoo が登録されていない」という警告が表示される
正常状態
プラグイン更新後、または正しい画面チェック付きで読み込まれれば警告は消え、管理画面がすっきりする

上記のようなデモの流れで、管理画面の上部やQuery Monitorに突然エラーが出現する。

WooCommerceは商品編集画面や注文編集画面といった限定された管理ページにのみ、wc-enhanced-selectやselectWooといった選択UIを拡張するためのスクリプトを登録している。ところがWPC Linked Variation 4.4.3は、admin_enqueue_scriptsアクションの中で画面を一切絞り込まずに、これらに依存するwpclv-backendを読み込もうとしていた。

その結果、WooCommerceがスクリプトを用意していない「割引プラグインの設定画面」や「ユーザープロフィール」など、まったく関係のないページで依存先が見つからず、WordPressが「WP_Scripts::add の呼び出しが正しくない」と警告を出す。フロントエンドの表示や動作には影響しないが、管理画面の見通しが悪くなり、Query Monitorなどのデバッグツールを使っていると特に目立つ。

プラグイン更新でエラーを消す手順

プラグイン更新でエラーを消す手順
STEP 1 WordPress管理画面「プラグイン」→「インストール済みプラグイン」へ移動する
↓
STEP 2 「WPC Linked Variation for WooCommerce」に更新通知が出ていたら「今すぐ更新」をクリックする
↓
STEP 3 更新後、管理画面を再読み込みしてエラーが消えたことを確認する

プラグイン一覧に更新通知が表示されない場合は、WooCommerce公式マーケットプレイスまたはCodeCanyonから最新版を手動でダウンロードし、FTP経由で上書きする方法もある。いずれの場合も、更新前に必ずサイト全体のバックアップを取得しておく。

バージョン4.4.4では、開発者がadmin_enqueue_scripts内に適切な画面チェックを実装し、WooCommerceの商品編集画面でのみ依存スクリプトを読み込むように修正された。そのため、他のプラグインの設定画面や投稿一覧などで無用な警告が出ることはなくなる。

すぐにアップデートできない場合の一時しのぎ

すぐにアップデートできない場合の一時しのぎ

プロジェクトの都合ですぐにプラグインを更新できない場合、応急処置としてQuery Monitorを一時的に無効化するか、プラグインファイルを手動で編集する方法がある。後者は更新時に上書きされてしまうため、あくまで次のアップデートまでのつなぎと考えてほしい。

コードを直接修正する開発者向け手順

WPC Linked Variationのプラグインフォルダ内にある wpc-linked-variation.php を開き、admin_enqueue_scripts にフックしている関数を以下のように変更する。現在の管理画面オブジェクトを取得し、投稿タイプが product の場合だけスクリプトを読み込む条件を追加する。

// 修正前(問題のあるコード)
add_action('admin_enqueue_scripts', 'wpclv_enqueue_scripts');
function wpclv_enqueue_scripts() {
    wp_enqueue_script('wpclv-backend', plugins_url('assets/js/backend.js', __FILE__), array('wc-enhanced-select', 'selectWoo'), WPC_Linked_Variation::VERSION, true);
}

// 修正後(画面チェックを追加)
add_action('admin_enqueue_scripts', 'wpclv_enqueue_scripts_fixed');
function wpclv_enqueue_scripts_fixed($hook) {
    $screen = get_current_screen();
    if ($screen && $screen->post_type === 'product') {
        wp_enqueue_script('wpclv-backend', plugins_url('assets/js/backend.js', __FILE__), array('wc-enhanced-select', 'selectWoo'), WPC_Linked_Variation::VERSION, true);
    }
}

この修正により、商品の編集・新規追加画面でのみスクリプトが読み込まれ、それ以外の管理画面ではエラーが発生しなくなる。修正後は管理画面をリロードして警告が消えることを確認する。本体のアップデートが可能になった時点で、必ず公式の最新版に差し替えることを推奨する。

よくある質問

このエラーはサイトのフロントエンドに影響しますか

影響しない。あくまで管理画面内でQuery MonitorやWordPressのデバッグ表示が警告を出すだけであり、来訪者が見る商品ページやチェックアウト画面の動作は変わらない。

プラグインを更新する以外の簡単な対処法はありますか

Query Monitorプラグインを無効化すればエラー表示自体は消えるが、あくまで表示上の回避に過ぎない。管理画面のパフォーマンスや他のスクリプト競合のリスクが残るため、プラグイン本体の更新が確実な解決策になる。

ほかのプラグインでも同様の「正しく呼び出されていません」エラーが出ます

多くのプラグインが管理画面用のスクリプトを読み込む際、WooCommerceや他のプラグインが提供するライブラリを依存関係に指定することがある。読み込むページを限定していない場合、同様の依存エラーが発生する。該当プラグインの開発者に報告するか、バージョンアップ情報を確認するのが近道だ。

バージョン4.4.4に更新してもまだエラーが消えない場合は

まずブラウザキャッシュとWordPressのキャッシュ(サーバーキャッシュやプラグインキャッシュ)をすべてクリアする。それでも消えなければ、別のプラグインが同様の問題を起こしている可能性があるため、すべてのプラグインを一時停止し、一つずつ有効化して原因を特定する。WooCommerce本体も常に最新版に保つ。

この記事のポイント

  • WPC Linked Variation 4.4.3以前に起因する管理画面のスクリプト依存エラー
  • プラグインを4.4.4以降にアップデートすれば完全に解消する
  • エラーはフロントエンドに影響せず、管理画面の表示だけの問題
  • 応急処置としてQuery Monitor無効化やコード修正も可能だが、公式更新が最も安全
  • 他プラグインでも同様の警告が起きる場合、画面チェックの有無を疑う
CSS疑似クラスでJavaScript代用、状態監視の全貌とevent-triggerの将来像

CSS疑似クラスでJavaScript代用、状態監視の全貌とevent-triggerの将来像

CSSは状態を追跡するための仕組みを着実に増やしている。かつてはJavaScriptでしか扱えなかったUIの変化を、疑似クラスだけで表現できる場面が広がっているのだ。

:hoverや:focusといった基本的なものから、:autofillや:volume-lockedのような新しい疑似クラスまで、その数は増え続けている。これらをJavaScriptイベントリスナーと比較しながら整理すると、CSSの設計思想がより明確に見えてくる。

ポインター系疑似クラス、UI反応の基本

ポインター系疑似クラス、UI反応の基本

マウスやタッチ操作に反応する疑似クラスは、ウェブデザインの基本だ。:hoverはpointerenterからpointerleaveまでの継続的な状態を表し、:activeは押下中の瞬間を捉える。これらは単なる一時的な出来事ではなく、ブラウザが認識する「状態」として設計されている。

:hoverと:activeの本質

CSS-Tricksの著者Carlo Daniele氏は、疑似クラスが「イベントではなく状態を追跡する」点を強調している。:hoverはpointerenterとpointerleaveという2つのJavaScriptイベントの間を継続的に監視する状態であり、:activeはpointerdownとpointerupの間の押下状態だ。この違いを理解すると、CSSとJavaScriptの適切な役割分担が明確になる。

通常状態(Before)
ボタン
↓
ホバー状態(After)
ボタン
■ 通常時 ■ ホバー時

上記のデモは静的な比較だが、実際の:hoverはカーソルが要素に乗っている間だけ継続する。JavaScriptではpointerenterとpointerleaveを個別に監視する必要があるが、CSSなら1行のセレクタで完結する。この簡潔さがCSSの強みだ。

pointer-eventsプロパティで反応を制御する

pointer-events: noneを指定すると、その要素はポインター関連のイベントを一切発火しなくなる。CSSで直接イベントの発生を抑制できる点は、JavaScriptでpreventDefaultを行うのとは異なる設計思想だ。装飾的なオーバーレイや無効化されたボタンなど、視覚的に存在するが操作対象ではない要素に有効な手法である。

フォーカス系疑似クラス、アクセシビリティの根幹

フォーカス系疑似クラス、アクセシビリティの根幹

フォーカス管理はユーザビリティとアクセシビリティの両面で重要だ。CSSの疑似クラスは、JavaScriptのfocus/blurイベントよりも直感的にフォーカス状態をスタイリングできる。

:focusと:focus-visibleの使い分け

:focusは要素がフォーカスを受け取った瞬間に発動するが、マウス操作でもキーボード操作でも一律に適用される。一方、:focus-visibleはブラウザのヒューリスティック(経験則)に基づき、フォーカスインジケーターを表示すべきかどうかを判断する。キーボード操作時にはアウトラインを表示し、マウスクリック時には非表示にする、といった制御がCSSだけで可能だ。

JavaScriptで同等の処理を書く場合、:focus-visible疑似クラスをmatchMediaで問い合わせる必要があるが、CSSならセレクタに:focus-visibleを追加するだけで済む。コード量の差は一目瞭然である。

:focus-withinと:has()の比較

:focus-withinは子孫要素がフォーカスを持っている場合に、親要素をスタイリングできる。フォーム全体をハイライトしたいときに便利だ。:has(:focus)も機能的には同等だが、:has()はより汎用的な「条件付き親セレクタ」として設計されており、フォーカス以外の条件にも対応できる。どちらを使うかは文脈次第だが、フォーカス特化の:focus-withinのほうが意図は明確になる。

フォームコンテナ(:focus-within未適用)
お名前
入力欄(未フォーカス)
↓
フォームコンテナ(:focus-within適用)
お名前
入力欄(フォーカス中)

このように、子要素のフォーカス状態を親要素のスタイルに反映させる仕組みは、JavaScriptでDOMトラバーサルを書くよりも圧倒的にシンプルだ。

フォーム系疑似クラス、バリデーションをCSSで扱う

フォーム系疑似クラス、バリデーションをCSSで扱う

フォームの入力チェックはウェブ開発の定番だが、CSSの疑似クラスを使えば、視覚的なフィードバックの大部分をJavaScriptなしで実装できる。:validや:invalidはもちろん、:user-validや:autofillといった新しい疑似クラスも登場している。

:checkedで切り替えUIを実装する

:checkedはチェックボックスやラジオボタンの選択状態を追跡する。JavaScriptのchangeイベントと異なり、CSSセレクタとしてスタイルに直接結びつく。CSS-Tricksの著者Carlo Daniele氏も指摘しているように、CSS疑似クラスは多くの場合、2つのJavaScriptイベントの間の状態を表現するが、時には条件分岐ロジックそのものを代替する。

未チェック状態
□
同意する
送信ボタンは無効
↓
チェック状態
✓
同意する
送信可能

この切り替えはJavaScriptのchangeイベントでcheckedプロパティを判定するのと同等だが、CSSではスタイルシート内のセレクタで完結する。コードの見通しが良くなる点が大きな利点だ。

:user-validと:autofill、ユーザー体験を高める新しい疑似クラス

:validや:invalidはページ読み込み直後から評価されるため、未入力の必須フィールドが即座に赤く表示される問題があった。:user-validと:user-invalidは、ユーザーが実際に値を入力しフォーカスを外すまで評価を遅延させる。JavaScriptのchangeイベントに近い挙動だ。

:autofillはブラウザの自動入力機能を検出するJavaScriptイベントが存在しない中で、CSSだけで自動入力されたフィールドをスタイリングできる貴重な手段である。パスワードマネージャーによる自動入力を視覚的に識別したい場合に実用的だ。

バリデーション状態の比較
:invalid 読み込み直後に即時評価(未入力でも赤くなる)
:user-invalid ユーザー操作後まで評価を遅延(実用的)
:autofill ブラウザ自動入力を検出(JSイベント無し)
■ 即時評価 ■ 遅延評価 ■ 自動入力検出

JavaScriptで同等の処理を実装する場合、checkValidity()メソッドやValidityStateオブジェクトを使うことになる。フォーム送信時に全体を検証するケースではJavaScriptが適しているが、入力中のリアルタイムフィードバックはCSSの疑似クラスに任せるほうが合理的だ。

メディア要素疑似クラス、動画と音声の状態をスタイリングする

メディア要素疑似クラス、動画と音声の状態をスタイリングする

HTML5の<audio>要素や<video>要素は、これまでJavaScriptで状態を監視しなければカスタムコントロールを作成できなかった。しかし、CSSのメディア要素疑似クラスが整備されつつあり、状況は変わりつつある。これらの疑似クラスはInterop 2026の対象にもなっており、ブラウザ間の相互運用性の向上が期待されている。

メディア疑似クラスとJSイベントの対応
CSS :buffering → JS waiting
CSS :muted → JS volumechange
CSS :playing → JS playing (playではない)
CSS :stalled → JS stalled
CSS :volume-locked → JS 専用イベントなし(要ダミー要素)
■ CSS疑似クラス ■ 対応するJavaScriptイベント

:volume-lockedは特に興味深い。JavaScriptで音量ロックを検出するには、ダミーのvideo要素を生成してvolumeを設定し、その値が反映されたかどうかを確認するという回りくどい方法が必要になる。CSSなら:volume-locked疑似クラスひとつで完結する。ブラウザ側の制約をCSSが抽象化してくれる好例だ。

インタラクティブ要素疑似クラス、ポップオーバーとダイアログの制御

インタラクティブ要素疑似クラス、ポップオーバーとダイアログの制御

近年HTMLに追加された<dialog>要素やポップオーバー機能は、CSSの疑似クラスと組み合わせることで真価を発揮する。:popover-open、:open、:modal、:fullscreenといった疑似クラスは、JavaScriptのtoggleイベントに頼らずにUIの状態をスタイリングできる。

閉じた状態(:open非該当)
ダイアログ(非表示)
開くボタン
↓
開いた状態(:open該当)
ダイアログ(表示中)
閉じるボタン
■ 非表示 ■ 表示中

JavaScriptで同様の処理を書く場合、toggleイベントを監視してから要素のopenプロパティをチェックする2段階の処理が必要になる。CSSの疑似クラスなら、スタイルシート内で直感的に記述できる。モーダルダイアログが開いているときに背景を暗くする処理も、:modal疑似クラスと::backdrop疑似要素の組み合わせで表現可能だ。

event-trigger、CSSに真のイベントリスナーが到来する未来

event-trigger、CSSに真のイベントリスナーが到来する未来

CSS-Tricksの著者Carlo Daniele氏が紹介しているAnimation Triggers仕様のevent-triggerは、現時点ではどのブラウザも未実装だが、CSSの状態監視を次の段階に引き上げる提案だ。これは従来の疑似クラスとは異なり、JavaScriptのイベントリスナーに近い働きをする。

event-triggerの基本構文

event-triggerは、CSSアニメーションを特定のイベントに結びつける仕組みである。event-trigger-nameでアニメーションの識別子を定義し、event-trigger-sourceで発火条件となるイベント(click、touch、dblclick、keypressなど)を指定する。アニメーションは通常その場で再生されるのではなく、イベントが発生するまで待機する。

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

button {    
  /* クリック時に --event アニメーションを発火 */
  event-trigger: --event click;
}

div {
  /* --event が発火したらアニメーションを前方再生 */
  animation-trigger: --event play-forwards;
  animation: fade-in 300ms both;
}
STEP 1 ボタンに event-trigger を設定
↓
STEP 2 クリックイベントで –event が発火
↓
STEP 3 animation-trigger がアニメーションを再生
↓
STEP 4 対象要素がフェードインアニメーションを実行

この流れは、従来のCSS疑似クラスが「状態」を追跡していたのに対し、明らかに「イベント」の発生をトリガーとしている点が新しい。clickイベントは元に戻せない不可逆的な出来事だが、interestイベントのように出入りがあるイベントの場合は、ステートフルな双方向トリガーも定義できる。

ステートレスとステートフルの2種類のトリガー

event-triggerには2つのモードが想定されている。ステートレストリガーはclickのような一度きりのイベント向けで、アニメーションは一方向にのみ再生される。ステートフルトリガーはinterestのような持続的な関心を表すイベント向けで、イベントの開始と終了に応じてアニメーションを前後に再生できる。

/* ステートフルトリガーの例 */
button {    
  event-trigger: --event interest / interest;
}

div {
  animation-trigger: --event play-forwards play-backwards;
  animation: fade-in 300ms both;
}

この構文では、interestの開始時にアニメーションを前方再生し、interestの終了時に逆再生する。ホバーでメニューがスライドインし、カーソルが離れるとスライドアウトするようなUIを、JavaScriptのイベントリスナーなしで実装できる可能性がある。

ステートレストリガー(click)
イベント クリック → アニメーション 前方再生のみ
ステートフルトリガー(interest)
開始イベント 関心を持つ → 前方再生
終了イベント 関心を失う → 逆再生
■ ステートレス ■ ステートフル

この仕様が実用化されれば、CSSのみで完結するUIコンポーネントの幅は大幅に広がるだろう。ただし、Carlo Daniele氏自身も記事内で触れているように、仕様は現在編集中であり、今後のドラフトで構成が大きく変わる可能性がある。現時点では構想段階の提案として捉えておくのが適切だ。

この記事のポイント

  • CSS疑似クラスはJavaScriptイベントの代替ではなく、状態を監視する独自のレイヤーとして進化している
  • :hoverや:focusのような基本疑似クラスから、:autofillや:volume-lockedのような新しい疑似クラスまで、対応範囲は拡大中
  • メディア要素疑似クラスはInterop 2026の対象であり、ブラウザ間の相互運用性が今後向上する
  • event-trigger仕様はCSSに真のイベントリスナーをもたらす提案だが、現時点では未実装であり将来の動向に注目すべき
  • CSSとJavaScriptは競合するものではなく、適材適所で組み合わせることで効率的なUI開発が可能になる
AutoptimizeのJavaScript最適化でドロップダウンメニューが動かない時の除外設定

AutoptimizeのJavaScript最適化でドロップダウンメニューが動かない時の除外設定

Autoptimize の「JavaScript コードを最適化」を有効にすると、ヘッダーのドロップダウンメニューが開かなくなる問題は、最適化処理がメニューを動かす JavaScript と競合するために起こる。ブラウザの開発者ツールで原因となるスクリプトを特定し、Autoptimize の「除外するスクリプト」欄にファイル名を追加すれば、最適化を維持したままメニューを正常に動作させられる。

なぜ JavaScript 最適化でメニューが動かなくなるのか

なぜ JavaScript 最適化でメニューが動かなくなるのか

Autoptimize の「JavaScript コードを最適化」は、複数の JavaScript ファイルを1つに集約し、不要な空白やコメントを削除する「縮小(ミニファイ)」を施す機能だ。加えて、読み込みタイミングをずらす「遅延読み込み」や「非同期読み込み」も合わせて適用される。

ドロップダウンメニューは、マウスのホバーやクリックを検知してサブメニューを表示する仕組みで、内部では jQuery やテーマ独自の JavaScript が複数連携して動作する。最適化によってこれらのスクリプトの実行順序が入れ替わったり、縮小処理中に特定の構文が破損したりすると、メニューを開くイベントが発火しなくなる。

特定のページだけで発生する仕組み

トップページや一部の内部ページでは正常に動き、キャリアページや特定の投稿ページだけでメニューが壊れる場合、落ちているのは集約後の順序問題であることが多い。ページごとに読み込まれるスクリプトの組み合わせが微妙に異なるため、特定の構成でのみ実行順序の破綻が表面化する。

jQuery 依存のメニューは特に影響を受けやすい

多くの WordPress テーマはメニューの開閉に jQuery を使う。Autoptimize の標準設定では jQuery も他のスクリプトと一緒に集約されるが、jQuery は他のスクリプトより先に読み込まれなければならない。集約順序が変わって jQuery が後回しになると、$ is not defined や jQuery is not defined といったエラーが発生し、メニュー全体が沈黙する。

原因となる JavaScript ファイルを特定する手順

闇雲に除外設定を増やすのは避けたい。まずはブラウザの開発者ツールでエラーの出どころを確認し、ピンポイントで除外するファイルを決める。

STEP 1 Chrome で問題のページを開き、F12 キーを押す
↓
STEP 2 「Console」タブをクリックし、赤いエラーを確認する
↓
STEP 3 エラーが示すファイル名(例: autoptimize_xxxxx.js やテーマの navigation.js)を記録する
↓
STEP 4 原因ファイルの元のパス(wp-content/themes/テーマ名/js/... 等)を特定する

エラーメッセージが「jQuery is not defined」であれば、jQuery の読み込み順がずれている。テーマ名や「navigation」「menu」を含むファイル名が表示されたら、そのファイルが縮小によって破損している可能性が高い。

エラーが出ない場合の切り分け方

コンソールにエラーが出ていないのにメニューが動かないケースもある。この場合は「Network」タブで autoptimize_xxxxx.js のレスポンスを確認し、途中で切れていないか、スクリプトの末尾が正常かを調べる。また、Autoptimize の設定で「JavaScript コードを最適化」だけをオンにし、「JavaScript を集約する」をオフにして症状が変わるかも試すと、問題の絞り込みが進む。

Autoptimize の除外設定でメニューを修復する

原因ファイルが特定できたら、Autoptimize の設定画面で除外リストに追加する。除外されたファイルは最適化の対象外となり、元の順序で単独で読み込まれるため、競合が解消する。

【Before エラー状態】
「JavaScript コードを最適化」オン
除外設定なし
→ ヘッダードロップダウンメニューが開かない
↓
【After 修正後】
「JavaScript コードを最適化」オン
除外リストに navigation.js と jquery.js を追加
→ 全ページでメニューが正常動作
■ エラー状態 ■ 修正後

除外設定の具体的な入力方法

WordPress 管理画面の「設定」→「Autoptimize」→「JavaScript オプション」を開く。「JavaScript コードを最適化」が有効になっている状態で、その直下にある「除外するスクリプト」欄に、カンマ区切りでファイル名を入力する。

入力例を以下に示す。実際のファイル名は、サイトのテーマやプラグイン構成によって異なる。

  • jquery.js 、 jQuery 本体
  • jquery.min.js 、 縮小版の jQuery
  • navigation.js 、 テーマのメニュー制御スクリプト
  • theme-menu.min.js 、 テーマが提供する縮小済みメニュー制御
  • js_composer_front 、 WPBakery 等のビルダーが出力するスクリプト

部分一致で指定できるため、jquery とだけ書けば、ファイル名に「jquery」を含むすべてのスクリプトが除外される。同様に navigation や menu といったキーワードでもよい。

「jQuery を集約しない」オプションの活用

Autoptimize の「JavaScript オプション」内には「jQuery を集約しない」というチェックボックスも用意されている。jQuery 依存のエラーが出ている場合は、個別のファイル名を書く前にまずこのチェックを入れてみると、まとめて解決することが多い。

どうしても直らない時の応用設定

除外設定を丁寧に行ってもメニューが復活しない場合、最適化モードそのものを調整する手がある。「JavaScript コードを最適化」の中には「縮小のみ(集約しない)」といった選択肢もあり、集約をやめて縮小だけに留めれば、多くの競合が回避される。

スクリプトの読み込み位置を変える

「JavaScript をフッターに移動する」や「Async(非同期)にする」といった項目も、メニューの動作に影響を与えうる。メニューはページの初期表示時に即座に動作する必要があるため、非同期読み込みにしてしまうと DOM 構築が完了する前にメニューのイベント登録が走ってしまい、動作しなくなる。まずはこれらのチェックを外して試す。

プラグイン単位での競合を疑う

まれに、Autoptimize と特定のキャッシュ系プラグインやテーマ付属の最適化機能が二重に働いて競合することがある。W3 Total Cache や WP Rocket に組み込まれた最適化と同時に使わず、いずれか一方に統一する。また、テーマの「パフォーマンス」設定内に JavaScript の最適化機能がある場合は、そちらを無効にして Autoptimize に一本化する。

よくある質問

除外設定を追加したのにメニューが直らない

Autoptimize のキャッシュが残っていると、除外設定が反映されずに古い最適化済みスクリプトが使われ続ける。管理画面の Autoptimize 設定画面で「キャッシュをクリア」ボタンを押し、さらにブラウザのキャッシュもスーパーリロード(Ctrl+F5)で破棄する。サーバーによっては CDN やサーバー側キャッシュもクリアする必要がある。

ファイル名がわからない時はどうするのか

ブラウザの開発者ツール「Network」タブで、JS ファイルの一覧を名前順に並べ、「theme」「menu」「nav」「dropdown」を含むファイルを探す。該当ファイルが見つからない場合は、テーマの開発者に「メニュー制御に使っている JavaScript ファイル名」を問い合わせるか、Autoptimize の「縮小のみ(集約しない)」モードで一旦回避する。

一部のページだけメニューが壊れるのはなぜか

ページによって読み込まれるプラグインやウィジェットのスクリプトが異なるため、集約後のファイルの構成が変わる。特定のページにだけ表示される「お問い合わせフォーム」や「求人一覧」のスクリプトが混ざると、集約後の全体の実行順序が崩れて、たまたまメニュー制御に影響が出ることがある。

Autoptimize を無効にするとサイトが遅くなるのが心配だ

JavaScript 最適化を完全に切る必要はない。問題のスクリプトだけをピンポイントで除外すれば、大部分のスクリプトは最適化されたまま配信される。PageSpeed Insights 等でスコアを確認しながら除外範囲を最小限に絞れば、速度と機能の両立は十分に可能だ。

この記事のポイント

  • JavaScript 最適化によるドロップダウンメニュー不具合は、スクリプトの順序破綻や縮小破損が原因
  • 開発者ツールの「Console」でエラーを特定し、原因ファイルを Autoptimize の除外リストに追加する
  • 「jQuery を集約しない」オプションが有効なケースも多い
  • 除外設定後は必ず Autoptimize キャッシュとブラウザキャッシュをクリアする
  • 「縮小のみ」「非同期読み込みオフ」など、最適化の段階を調整することでも解決できる
Prop For That、CSSで動的プロパティを扱う新ライブラリの全容

Prop For That、CSSで動的プロパティを扱う新ライブラリの全容

CSS-Tricksで紹介された「Prop For That」は、これまでのCSS設計の常識を塗り替える可能性を持つライブラリだ。ブラウザが本来CSS単体では取得できない情報、例えばマウスカーソルの座標やページのスクロール速度、現在時刻などを、あたかもネイティブのカスタムプロパティであるかのように扱えるようにする。開発者はライブラリを読み込み、対象のHTML要素に専用のデータ属性を付与するだけで、これらの動的な値を直接スタイルシートから参照できる。

CSS-Tricksの記事によれば、このライブラリの最大の魅力は、JavaScriptのロジックを意識せずに済む点にある。従来はイベントリスナーで値の変化を監視し、DOMのスタイルを逐次更新するスクリプトが必要だった。Prop For Thatを使えば、宣言的にCSSを記述する感覚のまま、高度なインタラクションを実装できる。本記事では、この新しいアプローチの仕組みや具体的な活用方法、そして現場への影響を掘り下げていく。

Prop For Thatが解決する根本的な課題

Prop For Thatが解決する根本的な課題

CSSは本来、ページが読み込まれた時点の静的なスタイルを定義する仕組みであり、ユーザーの操作に応じて刻一刻と変化するブラウザの内部状態を直接知覚できない。マウスポインターの位置、ページのどこまでスクロールしたか、特定のフォーム要素が今フォーカスを持っているかといった情報は、すべてJavaScriptの領分だった。この断絶が、アニメーションやインタラクションを実装する際のボトルネックになっていた。

従来のアプローチ(Before)
JavaScriptでイベントを監視し、スタイルを都度書き換える必要があった
開発者 JSで状態取得 → DOMのstyleプロパティ書き換え
課題ロジックとスタイルの分離が難しく、コードの見通しが悪くなる
↓
Prop For Thatのアプローチ(After)
HTML属性を付与するだけで、CSSカスタムプロパティとして値を参照できる
開発者 data-props-for属性を付与 → Prop For That CSS変数を自動更新
効果スタイルシート内で完結し、コードの凝集度が高まる

このデモが示すように、Prop For ThatはHTMLとCSSだけの世界観を維持したまま、動的な値を扱える設計思想を持つ。これは単なるユーティリティの追加ではなく、スタイリングの責務をCSSに取り戻すパラダイムシフトだ。

主要なライブプロパティとその仕組み

ポインタートラッキングで実現する追従型インタラクション

マウスカーソルの動きをCSSだけで捉えられると、ボタンのホバーエフェクトや視差効果の表現力が格段に上がる。Prop For Thatでは、data-props-for="pointer"という属性を設定した要素に対して、--live-pointer-xと--live-pointer-yという2つのカスタムプロパティが動的に注入される。

<div class="mover" data-props-for="pointer">...</div>
ポインター追従の概念
data-props-for=”pointer” → –live-pointer-x –live-pointer-y
使用例 要素の位置をカーソルに追従させる
left: calc(var(--live-pointer-x, 0) * 1px);
top: calc(var(--live-pointer-y, 0) * 1px);

これらの値はリアルタイムに更新されるため、要素をposition: absoluteで配置しておけば、CSSの計算式だけで物体がカーソルを追いかける動きを表現できる。マウスの速度に応じてスタイルを変化させるなど、従来は複雑なスクリプトが必要だった演出が、数行のスタイル宣言で完結する。

スクロールベロシティと現在時刻の活用

スクロールの勢いを表すベロシティ(速度)や、刻々と変化する現在時刻も、ライブプロパティとして取得できる。これらを活用すれば、ユーザーがページを勢いよくスクロールしているときだけ特定のアニメーションを発動させたり、時刻に応じて配色を動的に切り替えるといった演出が、CSSの範囲内で実装可能になる。

/* スクロール速度に応じて要素の透明度を変化させる例 */
.scroll-aware {
  opacity: calc(var(--live-scroll-velocity, 0) * 0.01);
  transition: opacity 0.3s ease;
}
スクロールベロシティ
–live-scroll-velocity → スクロールの勢い(px/frame)
用途勢いのあるスクロール時だけ要素を強調表示する
現在時刻
–live-time-seconds → 秒単位の現在時刻
用途時間帯に応じたダークモードへの自動切り替え

CSS-Tricksの記事で特に評価されていたのは、スクロールにモメンタム(慣性)の概念を持ち込める点だ。ユーザーの操作に物理的な手応えを感じさせる、いわゆる「気持ちいいインタラクション」の実装ハードルが大きく下がる。

実装のポイントとコード例

実装のポイントとコード例

基本的なセットアップ手順

導入は極めてシンプルだ。ライブラリをプロジェクトに読み込んだあと、動的な値を取得したい要素にdata-props-for属性を追加する。あとは通常のCSSカスタムプロパティと同じ感覚で、var()関数を使って値を参照すればよい。

<!-- HTML側 -->
<div class="tracker" data-props-for="pointer">
  この要素がカーソルを追跡する
</div>

/* CSS側 */
.tracker {
  position: absolute;
  width: 60px;
  height: 60px;
  background: #3498db;
  border-radius: 50%;

  /* ライブプロパティを参照して位置を動的に計算 */
  left: calc(var(--live-pointer-x, 0) * 1px - 30px);
  top: calc(var(--live-pointer-y, 0) * 1px - 30px);

  /* スムーズな追従のためのトランジション */
  transition: left 0.1s ease-out, top 0.1s ease-out;
}
設定フロー(After)
STEP 1 ライブラリをimportする
↓
STEP 2 HTML要素にdata-props-for属性を追加
↓
STEP 3 CSSでvar()を使ってライブプロパティを参照

このコード例では、カーソルを追いかける円形の要素を定義している。注意すべきは、var()の第2引数でフォールバック値(ここでは0)を指定している点だ。ライブラリが読み込まれる前や、何らかの理由でプロパティが未定義の場合でも、要素が想定外の位置に飛ぶのを防げる。

パフォーマンス上の配慮

ライブプロパティは高頻度で更新されるため、leftやtopのようなレイアウトを再計算させるプロパティの変更は、パフォーマンスの観点から注意が必要だ。可能であればtransformプロパティで位置を制御するほうが、ブラウザの合成処理に乗り、再描画コストを抑えられる。

/* パフォーマンスを考慮した書き方 */
.optimized-tracker {
  position: absolute;
  width: 60px;
  height: 60px;
  background: #e74c3c;
  border-radius: 50%;

  /* transformを使えばGPU合成で高速に描画される */
  transform: translate(
    calc(var(--live-pointer-x, 0) * 1px - 50%),
    calc(var(--live-pointer-y, 0) * 1px - 50%)
  );
}

このtransformによる制御は、特に多数の要素を同時に動かす場合や、モバイル端末での動作を考慮する際に有効だ。CSS-Tricksの紹介するデモ群でも、このベストプラクティスが採用されている。

Web制作の現場に与える影響

Web制作の現場に与える影響

JavaScriptとCSSの新たな役割分担

Prop For Thatの登場は、フロントエンド開発におけるJavaScriptとCSSの役割分担を見直す契機になる。従来は「動的なものはJavaScript、静的なものはCSS」という暗黙の線引きがあった。しかし、このライブラリが示す方向性は、表示やスタイルの変化はCSSに寄せるという考え方だ。

従来の役割分担(Before)
JavaScript 動的なスタイル変更を一手に担う
CSS 静的なスタイル定義のみ
↓
これからの役割分担(After)
JavaScript データの取得や状態管理に専念
CSS 動的なスタイル変更も含めて表示の責務を担当

これは単なる書き方の変化ではない。コードの凝集度が高まり、スタイルに関するロジックがCSSファイルに集約されることで、メンテナンス性が向上する。特に、複数人で開発する大規模プロジェクトや、インタラクションの多いランディングページの制作では、このメリットが顕著に現れる。

プロトタイピングスピードの加速

CSS-Tricksの記事が高く評価していたもう一つの側面は、プロトタイピングの速さだ。アイデアを思いついてから、実際にブラウザ上で動くモックアップを作るまでの時間が大幅に短縮される。複雑なJavaScriptの設定なしに、HTMLとCSSだけでリッチなインタラクションを試せることは、クリエイティブな探求の敷居を大きく下げる。

この手軽さは、デザイナーがコーディングに踏み出すきっかけとしても機能するだろう。また、クライアントワークの現場では、「この動きを実装するのにどれだけの工数がかかるか」という見積もりの精度も変わってくる。これまでスクリプトの作成で1日かかっていた表現が、数時間のコーディングで実現できる可能性があるからだ。

この記事のポイント

  • Prop For Thatは、マウス位置やスクロール速度などブラウザの動的情報をCSSカスタムプロパティとして参照できるライブラリである
  • 導入はライブラリの読み込みとHTML属性の追加のみで、JavaScriptの記述を必要としない
  • ポインタートラッキング、スクロールベロシティ、現在時刻など、多彩なライブプロパティが用意されている
  • パフォーマンスを考慮する場合は、leftやtopではなくtransformで位置制御するのが推奨される
  • JavaScriptとCSSの役割分担を見直し、スタイルの責務をCSSに集約する設計思想が背景にある
  • プロトタイピングの高速化や、インタラクション実装の工数削減といった実務的なメリットが大きい
JavaScriptがログアウト時に動かない原因と直し方

JavaScriptがログアウト時に動かない原因と直し方

管理画面にログインしているときだけ JavaScript が動き、ログアウトすると止まる。この現象の原因は、ほぼ「スクリプトの読み込み順序」と「キャッシュ・最適化プラグインの挙動」のどちらか、あるいは両方の組み合わせだ。ログイン時は管理バー用のスクリプト等が読み込まれるため依存関係が偶然成立し、ログアウト時にそれが外れてエラーになるケースが多い。

ログアウト時だけ JavaScript が動かなくなる仕組み

ログアウト時だけ JavaScript が動かなくなる仕組み
ログイン時(動作する)
jQuery → 管理バー用JS → スライダーJS
管理バー用のスクリプトが jQuery を読み込むため、後続のスライダーが依存できている
↓
ログアウト時(動作しない)
jQuery 未読込 → スライダーJS エラー
管理バーが無いため jQuery が読み込まれず、スライダーの処理が失敗する
■ ログイン時 ■ ログアウト時

このデモは典型的な依存関係の崩れを示している。ログイン中は WordPress が管理バーやフッターに jQuery を読み込むため、その後に記述されたスクリプトが偶然動く。ログアウトすると jQuery が存在せず、$ is not defined や jQuery is not defined といったエラーで止まる。

HTML ブロックに直接書いた JavaScript が招く問題

HTML ブロックに直接書いた JavaScript が招く問題

WordPress の「カスタム HTML」ブロックに <script> タグを直書きする方法は、一見手軽だが制御が難しい。出力される位置がテーマやブロック配置に依存し、jQuery などのライブラリより前に実行されれば必ず失敗する。さらにインラインスクリプトは多くのキャッシュプラグインで最適化対象から外されたり、結合・遅延読み込みの対象にならず、ログアウト時だけ二重に不利な状況を生む。

HTML ブロック直書きスクリプトの3つの弱点

  • 読み込み順序を制御できない(テーマの render 順に依存する)
  • jQuery の依存関係を WordPress に伝えられない
  • キャッシュ・圧縮プラグインがスクリプトとして認識しない場合がある

ログアウト時でも動くようにする正しい組み込み手順

ログアウト時でも動くようにする正しい組み込み手順

原則は「JavaScript は HTML ブロックに直書きせず、WordPress の仕組み(wp_enqueue_script)で読み込む」ことだ。すでに直書きで動いているものを移行するには、以下の手順で進める。

STEP 1 既存の script タグの中身を別ファイルに切り出す
↓
STEP 2 functions.php で wp_enqueue_script を使い、jQuery 依存を明示する
↓
STEP 3 $ の衝突回避のため即時関数または noConflict でラップする
↓
STEP 4 キャッシュプラグインの設定を見直し、スクリプトを除外する

STEP 1 既存の script タグを外部ファイルに移す

HTML ブロック内の <script>〜</script> 部分だけを抜き出し、子テーマのフォルダ内に testimonial-slider.js のような名前で保存する。<script> タグそのものは不要で、中身のコードだけを移す。HTML ブロックにはスライダーの構造(ul や div のマークアップ)だけを残す。

STEP 2 functions.php で安全に読み込む

子テーマの functions.php に以下のコードを追加する。管理画面ではなくフロントエンドだけに読み込ませるために wp_enqueue_scripts フックを使う。依存関係として jquery を指定すれば、WordPress 本体の jQuery が先に読み込まれてから実行される。

function my_testimonial_slider_script() {
    wp_enqueue_script(
        'testimonial-slider',
        get_stylesheet_directory_uri() . '/testimonial-slider.js',
        array('jquery'),
        '1.0.0',
        true
    );
}
add_action('wp_enqueue_scripts', 'my_testimonial_slider_script');

最後の引数 true はフッターで読み込む指定だ。スライダーの DOM 要素が本文中に存在する場合はフッター読み込みで問題ない。もしスライダーを本文より前に実行する必要があるなら false にしてヘッダーで読ませるが、多くのケースではフッターで十分だ。

STEP 3 $ の衝突を防ぐ

WordPress の jQuery は noConflict モードで動作しているため、$ がそのまま使えない環境がある。古いコードを流用している場合は $ is not a function エラーが起きやすい。回避策として、外部ファイル全体を即時実行関数で囲み、引数で $ を受け取る記法が安全だ。

(function($) {
    $(document).ready(function() {
        // ここにスライダーのコード
    });
})(jQuery);

STEP 4 キャッシュプラグインでスクリプトを除外する

ここまで対応しても直らない場合、キャッシュや最適化プラグインが原因の可能性が高い。ログアウト時はページキャッシュが有効になり、スクリプトの遅延読み込みや結合が適用される。自前の testimonial-slider.js をこれらの処理から除外する必要がある。

プラグインの設定画面で「スクリプトの除外」「遅延読み込みの除外」といった項目を探し、testimonial-slider(ハンドル名)または testimonial-slider.js(ファイル名の一部)を指定する。除外後は必ずキャッシュを全削除してからログアウト状態で確認する。

Elementor のフックやテーマのアクションフックを使った場合の注意点

Elementor のフックやテーマのアクションフックを使った場合の注意点

テーマ付属のフック(GeneratePress の Element など)に HTML ブロックごと差し込む方法も考えられるが、根本的にはスクリプトの読み込み順序問題は同じだ。フックで出力する位置を変えても、jQuery より前に呼ばれるリスクは残る。フックを使う場合でも、スクリプト部分は wp_enqueue_script に任せ、フックにはマークアップだけを出力する形が堅実だ。

Elementor Pro の「カスタムコード」機能を使っているなら、その中に script タグを書くのではなく、同様に子テーマのファイルとして切り出してハンドル登録するほうが制御できる。どうしても直書きが必要なら、カスタムコードの「場所」設定を「本文の終了タグ直前」にし、さらにコード内で jQuery を明示的に使う($ を使わない)ことでエラーを減らせる。

よくある質問

コンソールに「$ is not defined」と出るがどう直せばいいか

jQuery が読み込まれる前に $ を使っているか、noConflict モードで $ が無効になっている。即時関数で (function($) { ... })(jQuery); とラップし、すべての $ をこのスコープ内に収めれば解決する。

functions.php を編集せずに直す方法はあるか

「WPCode」などのコードスニペット管理プラグインを使えば、管理画面から wp_enqueue_script のコードを登録できる。functions.php を直接触りたくない場合の現実的な代替手段だ。スニペットの実行場所を「フロントエンドのみ」に設定するのを忘れないようにする。

キャッシュを削除しても直らないのはなぜか

ブラウザキャッシュだけを消していて、サーバー側のページキャッシュや CDN キャッシュが残っているケースが多い。WordPress のキャッシュプラグインの「すべてのキャッシュを削除」を実行し、さらに CDN を使っている場合はその管理画面からもパージする。シークレットウィンドウで確認するとブラウザキャッシュの影響を除外できる。

スライダーのマークアップだけ残して script を外したら表示が消えた

新しく作った JS ファイルが正しく読み込まれていない。ブラウザの開発者ツールの「ネットワーク」タブで testimonial-slider.js が 200 番で返っているか確認する。404 ならパスが間違っている。読み込まれているのに動かない場合は、コンソールに別のエラーが出ていないか調べる。

この記事のポイント

  • ログアウト時だけ JavaScript が動かない原因は、jQuery の依存切れとキャッシュ最適化の複合
  • HTML ブロックへの script 直書きは読み込み順序を制御できず、根本対策にならない
  • wp_enqueue_script で jQuery 依存を明示し、外部ファイルとして切り出すのが正攻法
  • 即時関数で $ の衝突を防ぎ、キャッシュプラグインでは独自スクリプトを除外対象に追加する
  • Elementor やテーマフックを使う場合も、スクリプトだけは enqueue に任せる設計が堅実
VoidZeroがCloudflareに参画、ビルドツールViteはオープンソースを維持

VoidZeroがCloudflareに参画、ビルドツールViteはオープンソースを維持

Vite、Vitest、Rolldown、OxcといったJavaScriptエコシステムの中核ツールを開発するVoidZeroが、Cloudflareに参画した。全チームメンバーがCloudflareに合流する大規模な動きだ。

この発表で最も強調されているのは、これらのプロジェクトがこれからもオープンソースであり続けるという点だ。ViteはMITライセンスを維持し、ベンダーに依存せず、コミュニティ主導で開発が進められる。この原則が揺らぐことはないとCloudflareは明言する。

背景には、AI時代のソフトウェア開発の変化と、フルスタック化するモダンアプリケーションの複雑さがある。Viteがエコシステム全体の共有基盤となった今、この参画はツールチェーンの未来を左右する重要な転換点だ。

VoidZeroのCloudflare参画の内容

VoidZeroのCloudflare参画の内容

オープンソースとベンダー中立の堅持

Cloudflareは、Viteや周辺ツールの独立性を何よりも優先するとしている。具体的な約束は以下の通りだ。

  • Vite、Vitest、Rolldown、Oxc、Vite+は今後もMITライセンスのオープンソースであり続ける
  • 特定のクラウドベンダーに依存しない設計を維持する。Viteで構築したアプリケーションは、どこでも動作し続ける
  • ロードマップはViteチームとコミュニティが引き続き主導し、公開の場で開発される
  • Evan You氏をはじめとするVoidZeroチームが、プロジェクトのリーダーシップを継続する
  • Cloudflareはこれらのプロジェクトにエンジニアリングリソースを投入するが、方向性を自社向けに曲げることはしない

Cloudflareのブログ記事では、このコミットメントを「言葉ではなく、日々の開発支援とプロジェクト運営で証明していく」と表現している。年初にAstroがCloudflareに参画した際と同様の、独立性を尊重するモデルだ。

100万ドルのViteエコシステム基金

この参画に伴い、CloudflareはViteエコシステム基金として100万ドルを拠出する。この基金はViteのコアチームによって管理され、メンテナーやコントリビューターへの支援に充てられる。

「ViteはVoidZeroやCloudflareよりも大きな存在だ」とCloudflareは述べており、エコシステムを支える無数の開発者を巻き込む意図が明確に示された。オープンソースの持続可能性を金銭面から支える、具体的な施策である。

従来の買収モデル(Before)
プロジェクトがベンダーに取り込まれ、徐々に特定サービスへの依存が強まる
ライセンス変更 ロードマップ非公開 コミュニティ離脱
↓
VoidZeroのCloudflare参画(After)
MITライセンスとコミュニティ主導を明文化し、独立性を資金面からも保証する
MITライセンス維持 ベンダー中立 100万ドル基金
■ 従来の懸念点 ■ 今回の保証

この比較から分かるように、今回の参画はエコシステムの信頼を損なわないための設計が徹底されている。Viteが多くのフレームワークに採用されている共有基盤だからこそ、中立性の維持は絶対条件となる。

AIが変えたビルドツールの役割

AIが変えたビルドツールの役割

エージェントがツールチェーンを回す時代

Cloudflareのブログ記事は、Viteの驚異的な普及の背景にAIの存在があると分析する。現在Viteの週間ダウンロード数は約1億2900万回、Cloudflare Viteプラグイン(@cloudflare/vite-plugin)は約1400万回に達している。これはVite本体のダウンロード数の10%を超える規模だ。

この急成長を牽引しているのが、AIコーディングエージェントだ。開発者だけが使っていた開発サーバーやリンター、フォーマッターを、今やAIエージェントが常時利用している。彼らはプロジェクトのスキャフォールディングから開発サーバーの起動、エラー解析、テスト実行までを自動で行う。

エージェントにとって重要なのは、高速なフィードバックループだ。ビルドが速く、テストが速く、エラーが明確で、CLIの挙動が一貫していること。VoidZeroのツールチェーン(Vitest、Rolldown、Oxc、Oxlint、Oxfmt)は、まさにこの要件に最適化されている。それぞれのカテゴリで最速クラスの性能を持ち、エージェントが何度も繰り返し実行してもストレスが少ない。

AIエージェント コード生成 → Vite 高速ビルド → Vitest 即時テスト
↓
AIエージェント エラー解析 → Oxlint 高速リント → 修正 再実行
■ AIエージェント ■ ビルドツール ■ 品質ツール ■ テスト

この図は、AIエージェントがコード生成からテスト、リント、修正までのサイクルを高速に回す様子を表している。各ツールの応答速度がエージェントの生産性に直結するため、VoidZeroのツールチェーンが選ばれる理由が明確になる。

フルスタック化するViteとVoidの知見

フルスタック化するViteとVoidの知見

ビルドツールを超えた役割

モダンなアプリケーションは、単なる静的ファイルのバンドルでは完結しない。サーバーサイドレンダリング、API、バックグラウンドジョブ、キュー、データベース、オブジェクトストレージ、リアルタイム通信、認証、そしてAIエージェントの統合までが必要になる。

Viteはこれに対応するため、ビルドツールからフルスタックアプリケーションの基盤へと進化しつつある。Cloudflareはこの流れを加速させるために、Vite本体にプロバイダ非依存の抽象化レイヤーを追加していく方針だ。バックエンド、API、エージェント、デプロイメントのためのフックをVite側に用意し、各クラウドベンダーがそれを実装する形を目指している。

すでにVoidZeroが実験していた「Void」プラットフォームの知見が、この方向性を後押ししている。VoidはVite向けのデプロイメントプラットフォームとして設計され、モダンアプリのライフサイクル全体を一つのツールチェーンで統一する試みだった。Cloudflareは将来的にこのVoidプラットフォームをオープンソース化し、誰でも独自のプラットフォームをVite上に構築できるようにする計画も示している。

Workerd統合による開発体験の向上

CloudflareとViteの協業は2024年のVite Environment APIから始まっている。このAPIによって、Viteの開発サーバーはNode.js以外のランタイムでもサーバーコードを実行できるようになった。

Cloudflare Viteプラグインを使用すると、vite devの実行時にサーバーコードがWorkerd(Cloudflare Workersのオープンソースランタイム)上で動作する。Durable Objects、D1、KV、R2、Workers AI、エージェントなど、本番環境と同じランタイムモデルがローカルで再現される。開発環境が本番の劣化版だった時代は、このAPIによって終わりを迎えつつある。

従来の開発フロー(Before)
開発PC Node.jsで開発
↓
本番環境 Workersランタイム
※ 環境差異による予期せぬバグが発生
↓
Environment API導入後(After)
開発PC Workerd上で開発
↓
本番環境 同一Workersランタイム
※ 開発と本番のランタイムが完全一致
■ 環境差異あり ■ 環境一致

この図が示すように、Environment APIの導入前後で開発体験は大きく変わった。ローカル環境と本番環境のランタイムが一致することで、デプロイ後の予期せぬエラーが激減する。

CloudflareがVite基盤に移行する意味

CloudflareがVite基盤に移行する意味

CLI統合とcfコマンドの未来

Cloudflareは自社ツールの方向性を「Viteに合わせる」と明確に宣言している。最近テクニカルプレビューが公開された新しい統合CLI「cf」は、Viteを基盤として設計される。

このCLIの目指す姿は、ViteのエルゴノミクスをそのままCloudflareプラットフォーム全体に拡張することだ。cf devはvite devのスーパーセットとして動作し、同じ速度、同じホットモジュールリプレースメント、同じプラグインモデルを持ちながら、必要に応じてCloudflareのランタイムとバインディングを利用できる。cf buildはViteプロジェクトをネイティブに理解し、cf deployはViteアプリのデプロイをシンプルにする。

すでにCloudflareのダッシュボード自体がVite上に構築されており、OxlintはCloudflareのコードベースで「数日分のエンジニアリング時間を節約している」と報告されている。Astroチームのエージェントハーネスフレームワーク「Flue」もVite基盤に移行中だ。Cloudflare自身がViteをドッグフーディングし、その価値を内部で証明している。

短期的な影響と長期的な展望

短期的には、Viteユーザーにとって何も変わらない。Vite、Vitest、Rolldown、Oxc、Vite+は引き続きリリースされ、VoidZeroチームがこれらを主導する。Cloudflare Viteプラグインも改善が続き、Environment APIもCloudflare以外のランタイムを含めて進化していく。

長期的には、CloudflareのCLIがVite上に完全に統合される。Viteにはフルスタックアプリとエージェントのためのプロバイダ非依存のプリミティブが追加され、あらゆるプラットフォームで利用可能になる。そしてVoidプラットフォームがオープンソース化され、誰でもViteとCloudflareの上に独自のプラットフォームを構築できるようになる。

この計画が実現すれば、ViteはJavaScriptエコシステムの単なるビルドツールから、アプリケーション開発全体を支える普遍的な基盤へと進化する。Cloudflareのインフラは、その基盤の上で最も統合された選択肢の一つとして位置づけられることになる。

この記事のポイント

  • VoidZeroの全メンバーがCloudflareに参画。ViteやVitestなどのツールはMITライセンスのままでベンダー中立を維持する
  • CloudflareはViteエコシステム基金として100万ドルを拠出し、コミュニティ主導の開発を資金面から支援する
  • Viteの週間ダウンロード数1億2900万回の背景には、AIエージェントによる高速フィードバックループ需要がある
  • Environment APIにより、ローカル開発環境でも本番と同じWorkerdランタイムが使用可能になった
  • Cloudflareの新CLI「cf」はViteを基盤に統合され、全プラットフォームで一貫した開発体験を提供する計画だ