Shooting Mode

Haste not, Rest not.

Author by Takayuki Higashi

Web Creator, Tokyo based,
Creative Director at YUIDEA Inc.

Webとデザインの話

WebP対応などWP高速化のためにやったこと

より多くの環境でできる限り速くページを表示したい、というのはWeb制作者なら誰もが考えること。もちろん、それ相応のデザインクオリティを保って、可能な限り見せ方にだってこだわりたい。
WordPress(WP)のようなフレームワークを使うと便利なプラグインがたくさんあるので、ついつい「あれもやりたい」「これもやりたい」と必要以上にモリモリなサイトになりがちですが、モノづくりには最終的に不要なものを見極めて削っていく引き算の作業がとても大切だと思うのです。

Google PageSpeed Insightsの指摘事項をヒントにしてサイトパフォーマンスを最適化

真を撮って載せるために作ったブログですが、結局3月後半はサイトとしての完成度が気になってしまって、サイト全体のパフォーマンス最適化を施していました。これはもう完全なる職業病。
一旦、気がすむまでやれることはやったと思えてきたので、備忘録を兼ねてWordPress高速化のために行ったことをまとめておきます。

高速化の指標にしたのは、GoogleのPageSpeed Insightsのスコアです。

PageSpeed Insightsで指摘されたことを少しずつ直していって、今はモバイルのスコアが80前後、デスクトップはよい時は90以上のスコアが出るようになりました(トップページの分析結果)。最適化のためにやったことは主に以下の4つです。

  • ページの表示に不要なリソースをなるべく読み込まない
  • 画像のファイルサイズをできる限り小さくする
  • 更新頻度の低いファイルはCDNのキャッシュを利用する
  • レンダリングをブロックする可能性のある要素は非同期で読み込むようにする

それでは、一つひとつ細かく見ていきましょう。

functions.phpでページの表示に不要な機能を無効化する

WordPressはとても便利ですが、要らないコードもたくさん吐き出します。
functions.phpに以下のような記述を行って、このブログに不要な機能のために自動生成されるHTMLタグは無効化しています。

<head>タグ内の不要なタグを除去

remove_action('wp_head', 'wp_generator'); // WordPressのバージョン情報を表示しない
remove_action('wp_head', 'print_emoji_detection_script', 7); // 絵文字用のJSを無効化
remove_action('wp_print_styles', 'print_emoji_styles', 10); // 絵文字用のCSSを無効化
remove_action('wp_head', 'rsd_link'); // RSDを無効化
remove_action('wp_head', 'wlwmanifest_link'); // Windows Live Writerを無効化

Embed機能の無効化

add_filter('embed_oembed_discover', '__return_false');
remove_action('wp_head','rest_output_link_wp_head');
remove_action('wp_head','wp_oembed_add_discovery_links');
remove_action('wp_head','wp_oembed_add_host_js');

ビジュアルエディタ用のスタイルの読込を無効化

function dequeue_plugins_style() {
  wp_dequeue_style('wp-block-library');
}
add_action('wp_enqueue_scripts', 'dequeue_plugins_style');

WordPressが自動で読み込むjQueryを無効化

function my_delete_local_jquery() {
  wp_deregister_script('jquery');
}
add_action('wp_enqueue_scripts', 'my_delete_local_jquery');

jQueryはGoogleにホストされているCDN版を読み込むことにしたので、WordPressが自動で読み込もうとするものは無効化しました。

functions.phpのフロント画面に関わりそうな記述は概ねこんな感じです。

EWWW Image OptimizerでWebP画像を生成する

写真を多く掲載する当ブログで、高速化のボトルネックとなるのがJPEG画像のファイルサイズの肥大化です。
スナップショット写真のJPEG画像はPhotoShopで書き出していますが、「Webおよびデバイス用に保存」メニューを使って、大きくてもファイルサイズが200KB程度になるようにしています。
さらにEWWW Image Optimizerというプラグインを利用して、JPEG画像の最適化とWebP画像の生成を自動化しています。

WebPは「ウェッピー」と呼ぶそうですが、Googleが開発した静止画のフォーマットです。JPEGよりもファイルサイズが小さくなるケースが多く、当ブログでもWebP対応ブラウザにはWebP画像を表示するようにしました。

問題はIEとSafariがWebPに対応していないという点。IEはともかくiPhoneユーザー(≒Safariユーザー)の多さは無視できません(かくいう私もiPhoneユーザー)。
画像はLazyLoadを使って遅延読込を行っているので、LazyLoadが実行される前にWebP対応ブラウザについてはJPEGをWebPに書き換えてしまおう、ということでJavaScript(jQuery)を書いてみました。

初期状態ではLazyLoadが画像を読み込むためのdata-original属性に.jpgを指定しておき、後からjQueryでユーザーエージェントを見てWebP対応ブラウザに対しては画像のファイル名末尾に.webpを付与します。

jQueryの記述例は以下の通り。

var userAgent = window.navigator.userAgent.toLowerCase();
var msie = userAgent.indexOf('trident') != -1; // IEである場合
var safari = userAgent.indexOf('safari') != -1 && userAgent.indexOf('chrome') == -1; // Safariである場合
var supportWebpBrowsers = !(msie || safari); // IEまたはSafariではない

// 注)テストなのでユーザーエージェントによるブラウザ判定はざっくりです

$(function() {
  // replaceJpg()を実行してからlazyLoad()を実行する
  $.when(
    replaceJpg()
  ).done(function() {
    lazyLoad()
  });
});

// .jpgを.jpg.webpに置き換える関数
function replaceJpg() {
  if(supportWebpBrowsers) {
    $('img.lazy').each(function() {
      var originalImg = $(this).attr('data-original'); // imgタグのdata-original属性を取得
      var webpImg = originalImg + '.webp'; // ファイル名の末尾に.webpを付与する
      $(this).attr('data-original', webpImg); // data-original属性を.jpg.webpにする
    });
  };
};

// LazyLoadを実行する関数(フェードインで画像を表示)
function lazyLoad() {
  $('img.lazy').lazyload({
    effect:'fadeIn'
  });
};

このコードの動作確認用のHTMLを作ってみました。

なお、このテストではアプリのWebViewについては考慮していません。WebViewまで考慮して画像を出し分けるためには、もう少し細かくユーザーエージェントを見て条件設定する必要があります。

サーバーサイドでJPEGとWebPの出し分けを行わなかったのはCDNを使っているからです。
アクセスしてくるブラウザによって毎回、画像名を変更する可能性のあるHTMLだとキャッシュができません(キャッシュされたら困る)。

CDNで更新頻度の低いリソースをキャッシュする

CDNはContent Delivery Networkの略。要するにキャッシュを使ってデータの配信を高速化する仕組みです。
当ブログではCloudflareの無料プランを利用しています。キャッシュの有効期限は30日間に設定。

画像はもちろんCSSやJSなどもキャッシュされるため、更新を行う場合には都度任意のパラメータを付与するなどしてファイル名を変更する必要があります。
手動でキャッシュをパージ(削除)するには管理画面から操作を行う必要がありますが、WordPressのプラグインを利用すれば、WordPressの更新をトリガーにしてキャッシュを自動でパージしてくれるようです。

また、WebフォントとjQueryはCDNから配信されているものを読み込んでいます。
まずjQueryについては以下のようにfunctions.phpに記述してwp_footer()(つまり、<body>タグの後方)で読み込むようにしています。
ページのレンダリングに影響しないJavaScriptはなるべくページの後方で読み込ませるのが基本。

function loadJquery() {
  // CDNのjQueryを読込
  wp_enqueue_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js', array(), '', true);
};
add_action('wp_enqueue_scripts', 'loadJquery');

jQueryの読み込み方はシンプルだけど、CSSの読み込み方は少し工夫が必要になります。

外部ドメイン(CDN)のCSSを非同期で読み込む

WebフォントについてはCSSのサイズが大きいため、普通に<head>タグ内で読み込んでしまうと、Google PageSpeed Insightsはこれがページのレンダリングを遅らせている要素だとして改善を促してきます。

CSSを非同期で読み込むにはいろんなテクニックがありますが、今回は一度もやったことがない方法でCSSの非同期読込にチャレンジ。「その手があったか!」と感心しました。

まず<link rel="preload">でCSSを読み込みます。 そして、preloadが未対応のブラウザに対応するために<link rel="stylesheet" media="print" onload="this.media=’all’">で印刷用としてCSSを非同期で読み込み、後からJavaScriptで全メディア用のCSSに書き換えるというテクニック。
当ブログもこの例にならって、GoogleとCloudflareで配信されている計3つのWebフォント用CSSを以下のようにして読み込んでいます。

function load_external_css() {
  // Noto Sans (Google Fonts)
  $notoSansJp = 'https://fonts.googleapis.com/css?family=Noto+Sans+JP:400,500,700&subset=japanese&display=swap';
  // Doppio One (Google Fonts)
  $doppioOne = 'https://fonts.googleapis.com/css?family=Doppio+One&display=swap';
  // Font Awesome (Cloudflare)
  $fontAwesome = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/solid.min.css';
  // CSSを読込
  $externalCss = array( $notoSansJp , $doppioOne , $fontAwesome );
  foreach ($externalCss as $value) {
    echo '<link rel="preload" href="' . $value . '" as="style"><link rel="stylesheet" href="' . $value . '" media="print" onload="this.media=\'all\'">' . "\n";
  };
};
add_action('wp_head', 'load_external_css');

写真を多く掲載して見せたいブログなので、画像を点数・ファイルサイズともこれ以上削るわけにもいかず、このぐらいが工夫の限界かなと思います。

以上、前身ブログ以来久しぶりのコーディングネタでした。この備忘録がもしも誰かのお役に立てるのなら幸いです。

COMMENTS

Leave a Reply

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です