WordPressでCSSとJSのキャッシュ対策を行う

JavaScriptファイルやCSSファイルをCDNにキャッシュさせるケースはよくあると思います。その場合、同じファイル名だと更新してもキャッシュが効いてしまって更新されません。

対策はいくつかあります。

ファイル名のGETパラメータにタイムスタンプを加える

<link rel="stylesheet" href="<?php site_url(); ?>/assets/css/main.css?date=<?php echo date(YmdHis); ?>">

目的は達成できていますが、ブラウザにキャッシュさせることができず、都度ファイルを取得してしまいます。パフォーマンス面は最悪です。

手動でバージョンを記述する

<link rel="stylesheet" href="<?php site_url(); ?>/assets/css/main.css?v=1.1 ?>">
目的も達成できて、GETパラメータを更新しない限りキャッシュされるのでパフォーマンス面も問題ありません。ただし手動なのでとても面倒です。CSSファイルを更新するたびに手動でPHPファイルも更新する必要があります。メンテナンス面は最悪です。

webpackのマニュフェストプラグインを利用する

RailsではCSSは以下のように乱数付きで読み込まれます。
<link rel="stylesheet" media="all" href="/packs/application-745e2ece205a5546f234.css" />
ビルドを実行するたびに、この乱数(ここではハッシュと呼びます)がアップデートされます。この素敵な機能をWordPressで再現してみます。

1. webpackでハッシュ付きファイルを出力する

`webpack-manifest-plugin` を使うと、乱数付きのファイル名をマップするJSONファイル(manifest.json)を生成することができます。以下はwebpack.config.jsのサンプルです。
const path = require("path");
const ManifestPlugin = require("webpack-manifest-plugin");

const output = "wordpress/assets";

module.exports = {
  mode: "production",
  entry: {
    main: ["./frontend/js/main.js", "./frontend/scss/main.scss"]
  },
  output: {
    filename: "js/[name]-[hash].js",
    path: path.resolve(__dirname, output)
  },
  plugins: [
    …省略,
    new ManifestPlugin({
      fileName: "manifest.json",
      publicPath: "/assets/",
      writeToFileEmit: true
    })
  ],
  module: {
    rules: [
      ...省略
    ]
  },
  resolve: {
    extensions: [".js", ".json", ".css", ".scss"]
  }
};
このようなmanifest.jsonが出力されます。
{
  "main.css": "/assets/css/main-7e90a4e689517fb8f656.css",
  "main.js": "/assets/js/main-7e90a4e689517fb8f656.js"
}
WordPress側でこのJSONをロードすればOKです。

2. WordPressからmanifest.jsonをロードする

functions.phpに以下の内容を追加します。
function wp_bundle_tag($target_file_name) {
  $manifest_json = '/../../../../assets/manifest.json';
  $json_path = realpath(__DIR__.$manifest_json);
  $json = file_get_contents($json_path);
  $hash = json_decode($json, true);
  $file_url = $hash[$target_file_name];
  $ext = substr($target_file_name, strrpos($target_file_name, '.') + 1);
  if ($ext === 'css') {
    echo '<link rel="stylesheet" href="'.$file_url .'">'."\n";
  } else {
    echo '<script src="'.$file_url.'"></script>'."\n";
  }
}
テンプレートでこう使います。
<?php wp_bundle_tag(‘main.css’); ?>
こう出力されます。
<link rel="stylesheet" href="/assets/css/main-0c98704719ee736631e7.css">
これでRailsのようなキャッシュバスター機能をWordPressで実現することができました。

番外. 開発環境と本番環境の切り分け

私は、開発環境時はこの機能を利用しないでmain.cssをロードするようにしています。本番環境時のみ、ハッシュ付きファイルをロードします。開発環境と本番環境の切り分けは、単純にwp-config.phpに以下のような環境変数を定義します。

define('ENV', 'production');

wp-config.phpはGit管理をしないため環境変数を定義するのに使えます。このENV変数はグローバル変数なので、上のwp_bundle_tag関数内からでも使うことができます。そこで環境ごとに処理を切り分けます。

まとめ

株式会社トゥーアールではwebpackやDockerを利用したWordPress開発をおこなっています!お気軽にご相談ください!