ワードプレスのブログやサイトを高速化するCSS・JSファイルを結合する方法

CSS JS ファイルマージ 速度改善
CSS JS ファイルマージ 速度改善

予めコードを保存しておき、1コールで呼び出せるようにしておくことで、ページの速度がずいぶん改善されましたので、その方法を紹介しておこうと思います。当ブログでも使用中の「Optimizer」で使用されている方法です。

コピペで出来るようにするつもりですが、管理画面と公開ページ側での処理を分離することが目的ですので、テーマなどもある程度分離できている必要があります。このテーマ「Optimizer」でも、出来る限り読まないようにしていますので、ファイルマージのみで全てが改善されるとは思いませんが、沢山のCSS・JSファイルをエンキューしているのであれば、改善の余地があるかもしれません。

一応当ブログのページ速度も「PageSpeed Insights」で計測できますので、ページ速度改善の指標にしてください。

先に書いておきますが、バッファーで取得したコンテンツからURLを取得する方法とは異なり、操作方法にちょっと癖が出てしまうのですが、カスタマイズが少し出来る人であれば簡単ですし、改善もし易いと思います。

また、僕自身はクラスを作成して呼び出していますので、実際に僕が使っている形とは若干異なります。クラス内に実装する場合は、「add_filter」や「add_action」、その他変数などは自分で編集してください。

また、ワードプレスをご利用でしたら、このファイルマージ機能を改良したインライン出力プラグイン「Magic Inliner」もご検討ください。

  • Table of Contents

ちょっと癖のある方法とは

「ファイルを結合する」とありますが、Wordpressでの問題点として、公開ページ側で出力するためにエンキューするファイルに与えられるハンドルやURLを管理画面で直接取得する方法が見当たらないことです。

あれば楽になるし、僕もそれを使いたいところなんですが、中々そうはいきません。

そこで公開ページ側をロードする際に「style_loader_tag」や「script_loader_tag」で一度ハンドルを取得し、そのハンドルを下にファイルへアクセスして、コンテンツを取得するという方法です。こうすることで実際に出力されるタグが確認できますし、ハンドルをページやデバイスによって分別して保存することで、自分のWordpress環境に最適な条件でスクリプトをまとめて置くことも出来ます。

一応先にコピペした機能を適応させるための条件として、先に固定値を設定しておきましょう。

// 設定を取得
if( ! defined( 'IS_MERGE_CSS_ON' ) ) { define( 'IS_MERGE_CSS_ON', false ); }
if( ! defined( 'IS_MERGE_JS_ON' ) ) { define( 'IS_MERGE_JS_ON', false ); }

これは「false」になっていますので、設定が適用されない状態になっています。

実際に使用する際は「false」の部分に、テーマやプラグインの設定で真偽値を取得出来るようにしておくと管理しやすくなります。

公開ページ側での処理

公開ページ側の処理は管理画面で使用するための「名前」「ハンドル」「URL」を取得するように書いていきます。

また、同時に管理画面で保存したCSS・JSを出力するコードも載せてあります。

/*
 * 公開ページ側
 */
if( ! is_admin() ) {

	// 出力用
		add_action( 'wp_head', 'output_custom_registered_styles' );
		add_action( 'wp_footer', 'output_custom_registered_scripts' );
		function output_custom_registered_styles() {
			echo '<style>' . get_option( 'custom_merged_styles_string' ) . '</style>';
		}
		function output_custom_registered_scripts() {
			echo '<script>' . get_option( 'custom_merged_scripts_string' ) . '</script>';
		}


	// 取得用
		// CSS用
		$registered_css = array();
		$registered_css_handles = get_option( 'registered_enqueued_css_handles' );
		$registered_css_count = 0;
		// JS用
		$registered_js = array();
		$registered_js_handles = get_option( 'registered_enqueued_js_handles' );
		$registered_js_count = 0;


		// CSSとJSのデータを保存
		add_action( 'wp_footer', 'register_enqueued_js_css_data' );
		function register_enqueued_js_css_data() {

			# スタイル
				// CSSのデータ取得用
				global $registered_css;
				// ハンドルのカウント用
				global $registered_css_count;

				if( is_array( $registered_css ) ) {
					foreach( $registered_css as $name => $data ) {
						$num = $data[ 'num' ];
						$css_to_be_registered[ $num ] = $data[ 'href' ];
						$css_handles_to_be_registered_[ $name ] = $data[ 'handle' ];
					}	
				}
				// データとハンドルを保存
				if( ! is_customize_preview() ) { 
					update_option( 'registered_css', $css_to_be_registered );
					update_option( 'registered_css_handles', $css_handles_to_be_registered_ );
				}

			# スクリプト

				// JSのデータ取得用
				global $registered_js;
				// ハンドルのカウント用
				global $registered_js_count;

				if( is_array( $registered_js ) ) {
					foreach( $registered_js as $name => $data ) {
						$num = $data[ 'num' ];
						$js_to_be_registered[ $num ] = $data[ 'src' ];
						$js_handles_to_be_registered_[ $name ] = $data[ 'handle' ];
					}
				}
				// データとハンドルを保存
				if( ! is_customize_preview() ) { 
					update_option( 'registered_js', $js_to_be_registered );
					update_option( 'registered_js_handles', $js_handles_to_be_registered_ );
				}


		}


		// CSSタグのフィルター
		add_filter( 'style_loader_tag', 'get_enqueued_css_handles', 10, 2 );
		function get_enqueued_css_handles( $tag, $handle ) {

			// CSSのデータ取得用
			global $registered_css;
			// ハンドルのカウント用
			global $registered_css_count;
			// 登録済みCSSのハンドル
			global $registered_css_handles;

			// 「プレビュー以外」「(スイッチが有るなら)ONの場合」
			if( ! is_customize_preview() && IS_MERGE_CSS_ON ) {

				// ハンドル取得
				$registered_css_count++;
				$target = '/<link[^>]+href\s*=\s*[\'"]([^\'"]+)[\'"][^>]*>/i';
				if( preg_match( $target, $tag, $matched ) ) {
					$registered_css[ $handle ] = array( 'num' => $opt_registered_css_count - 1, 'handle' => $handle, 'href' => $matched[ 1 ] );
				}

				// 登録済みのハンドルの場合
				if( is_array( $registered_css_handles ) ) { 
					if( in_array( $handle, $registered_css_handles ) ) {
						return $tag;
					}
				}
			}

			return $tag;
		}

		// JSタグのフィルター
		add_filter( 'script_loader_tag', 'get_enqueued_js_handles', 10, 2 );
		function get_enqueued_js_handles( $tag, $handle ) {

			// JSのデータ取得用
			global $registered_js;
			// ハンドルのカウント用
			global $registered_js_count;
			// 登録済みJSのハンドル
			global $registered_js_handles;

			if( ! is_customize_preview() && IS_MERGE_JS_ON ) {
				// 「管理バー」が出ている場合
				if( ! ( $handle == 'admin-bar' || $handle == 'theme_customizer_js' ) ) {

					$registered_js_count++;
					$target = '/<script[^>]+src\s*=\s*[\'"]([^\'"]+)[\'"][^>]*>/i';
					if( preg_match( $target, $tag, $matched ) ) {
						$registered_js[ $handle ] = array( 'num' => $registered_js_count - 1, 'handle' => $handle, 'src' => $matched[ 1 ] );
					}

				}
				// 登録済みのハンドルの場合
				if( is_array( $registered_js_handles ) ) {
					if( in_array( $handle, $registered_js_handles ) ) {
						return $tag; // 既に保存されている場合はスキップ
					}
				}
			}

			return $tag;

		}

}

まず最初に管理画面で保存したものを出力するコード、その次にハンドルなどを取得するためのコードが書いてあります。

コードにも書いてありますが、読ませるのは「! is_admin()」という条件で管理画面以外にしておきましょう。

また、注意点ですが、テーマカスタマイザーでのプレビュー画面(条件:is_customize_preview)では読ませないようにし、ログイン中に表示される管理バーのスクリプトは保存しないように気を付けましょう。無駄なコードが増えてしまいます。

管理画面側での処理

こちらは公開ページ側で保存したデータを元に、ファイルマージしてスクリプトを保存する仕組みを用意します。

/*
 * 管理画面用
 */
if( is_admin() ) {

	add_action( 'admin_init', 'manage_css_data' );
	function manage_css_data() {

		// 公開ページで取得したデータ
		if( IS_MERGE_CSS_ON ) { // 条件を与える
			$registered_css = get_option( 'registered_css' );
		} else {
			delete_option( 'registered_css' );
			delete_option( 'registered_css_handles' );
			delete_option( 'custom_merged_styles_string' );
			return;
		}

		// 配列で取得し結合
		foreach ( $registered_css as $name => $url ) {
			$styles[ $name ] = preg_replace( '/(\n|\r|\t)/', '', file_get_contents( $url ) );
		}
		$custom_styles_string = implode( ' ', $styles );

		update_option( 'custom_merged_styles_string', $custom_styles_string );

	}


	add_action( 'admin_init', 'manage_js_data' );
	function manage_js_data() {

		$registered_js = get_option( 'registered_js' );

		// 公開ページで取得したデータ
		if( IS_MERGE_JS_ON ) { // 条件を与える
			$registered_css = get_option( 'registered_js' );
		} else {
			delete_option( 'registered_js' );
			delete_option( 'registered_js_handles' );
			delete_option( 'custom_merged_scripts_string' );
			return;
		}

		// 配列で取得し結合
		foreach ( $registered_js as $name => $url ) {
			$scripts[ $name ] = preg_replace( '/(\n|\r|\t)/', '', file_get_contents( $url ) );
		}
		$custom_scripts_string = implode( ' ', $scripts );

		update_option( 'custom_merged_scripts_string', $custom_scripts_string );

	}

}

ページや端末によって保存条件を変える場合は、自分で追加してください。

プラグインではページごとに読ませるコードを変更しているのは見たことがありませんが、もしかしたらそういうプラグインもあるかもしれませんので、念の為に条件を設けて保存する場所を複数用意して置いた方が良いかもしれません。

実装後の操作方法

公開ページ側で一度コードを読ませた後、取得したハンドルとURLからスクリプトを保存しますから、管理画面での操作は以下のようになります。

  1. 「IS_MERGE_CSS_ON」や「IS_MERGE_JS_ON」の真偽値を設定で「true」に変更
  2. 公開ページ側で一度ロード(ハンドルなどのデータを取得)
  3. 管理画面で一度ロード(取得したデータから、ファイルのスクリプトを結合して保存)

プラグインを新たにインストールしたり、アンインストールする場合は、不要・必要なハンドルなどを一旦リセットするために一度「IS_MERGE_CSS_ON」や「IS_MERGE_JS_ON」の真偽値を設定で「false」に戻して初期化して、再度上記の手順で再設定すれば大丈夫でしょう。

また、上の例だと管理画面のページを読む度に保存コードが読まれますので、Wordpress関数「set_transient」などで、保存する間隔や条件も設けておくと更に良いと思います。

他にも改善余地はあると思いますが、大体の仕組みはこんな感じです。一応コピペでも使えると思いますが、読ませる場所などがちゃんと管理できていればある程度改善できるんじゃないでしょうか。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください