2018年11月にGoogleのPageSpeed Insights(以下、PSI)が大幅リニューアルされた。LightHouseという今までChrome拡張などのオプションでしかなかったツールが標準導入され、チェック項目や採点の仕方が大きく変わったが、この対策についての情報が少なかったので、今回請け負った静的HTMLサイトの高速化で行なったことを記録に残しておきます。
Contents
次世代フォーマットでの画像の配信
jpg, jpeg, pngではなくGoogleが開発した新しいWebp形式の画像を表示するようにすればOK。静的なHTMLで記述したページの高速化の場合、以下のようなWebサービスやツールで画像をWebpに変換できるので、これを使って sample.jpg.webp
のような拡張子にして、現在の画像と同じフォルダに置いてあげればOK。
- XnConvert (法人利用は有料になったようです)
- Webp-Converter
ただしこのWebp形式自体は数年前からあるものの、現時点でまだChrome以外のブラウザでは非対応らしい(※1)。そのためhtaccessの記述などで jpe?g/png
とwebpを対応・非対応で出し分ける必要がある。コードは「webp htaccess」などでググれば多く出回っている。 https://doocts.com/3288https://qiita.com/miyanaga/items/6570c3c9ae8e15dbb57c
ちなみに本当にChromeなどの対応ブラウザでWebp画像が読み込まれているかは Chrome Developer Tool の Network タブを開いてページを再読み込みし、typeの列にwebpと表示されるかどうかで確認できます。
※1: 2024年現在、96%以上のブラウザでサポートされています。
WordPressの場合
もしWordPressでEWWW Image Optimizer を使っている場合は以下の項目にチェックを入れるだけで良しなに対応してくれる。
CDNを使っている場合
CloudflareなどのCDNサービスを使っている場合は注意が必要。Webpに対応したブラウザのユーザーがアクセスしてきたときにWebPの画像をキャッシュし、次にWebp非対応ブラウザ(iPhoneのブラウザはほぼ非対応)のユーザーがアクセスしてきた時にCDNサービスはキャッシュ済みのWebP画像を渡してしまう。つまりユーザーに画像が表示されなくなってしまうので注意。(その場合はhtacessの書き方を工夫する必要があるが今回は割愛)
オフスクリーン画像の遅延読み込み
いわゆるLazyLoadで、一般的にはjQuery依存のLazyLoadライブラリが定番らしいですが、調べるなかでjQueryに依存しないlazysizesライブラリの方が軽くて記述も簡単そうでした。
背景画像も遅延読み込み可能。万能Lazyloader、lazysizesの使い方 | WEMO
しかし今回の案件はサイト上部でslick.js(jQuery依存のスライドショーのライブラリ)を使用していたので、ファーストビューのスライド部分では上手く遅延読み込みがでませんでした。そこでslick.jsに元々備わっている画像遅延機能を使ってsrc=""
をlazy-data=""
に置き換えることで解決しました。
ちなみに、echo.jsも試してみましたが、lazysizesに比べて画像のカクツキ(スクロール時の読み込み開始タイミングやスピードの遅さ)がひどかったので、やはりlazysizesがベストだと思います。
WordPressの場合
WordPressであれば LazyLoad by WP Rocket プラグインがおすすめ。他と比べてカスタムフィールドで出力した画像や関連記事のサムネイル画像などのあらゆる画像遅延ができ、ロード時のチラつきも気にならない。標準のthreshold
(スクロールが画像の何ピクセル上まできたら画像読み込みを開始するかの閾値)の設定が綺麗なのだろう。
ただしファーストビューに入る画像のうち、本文以外のロゴやサイドバーの画像まで遅延させてしまうので、これらをdata-no-lazy='1'
の付与によって無効化しなければならないのが少しやっかいです。functions.phpにてクラスの指定による除外フィルターの設定も出来るが、複数のクラスを指定する方法がどうにも分からなかったので、1つずつimg
タグに属性付与していくしか今のところ方法が分からない。
レンダリングを妨げるリソースの除外
これはこれまでもあった「レンダリングブロックしている(HTMLの描画を妨げている)CSSやJavaScriptを後から読み込ませる」もの。
JavaScriptのレンダリングブロック解除
まずは<script>
タグを</body>
タグの直前に1個ずつ手動で移動させました。またjQuery以外のscriptには非同期かつ順序読み込みのdefer
属性をつけて遅延させました。
ちなみにasync
属性だと同じ非同期でも読み込み順序が記述した順番通りになる保証はなく、それぞれのscriptが読み込み終わり次第バラバラに実行されて不具合の原因になる。しかしなぜかGoogleはasync
を推奨している。
jQueryは重要なスクリプトライブラリ(Critical Request Chainとか言うらしい)なので、初めに呼ばないとナビメニューやスライドショーが動かなくなったりする。よってこれの読み込み部分はheadの中に留めておいた方が無難(つまりjQueryによるレンダリングブロックは諦める)。
一応<head>
の中ではなく</body>
直前で呼べばエラーもなくPSIの警告も消えるらしいが、それで不具合が本当に起きないかは自信がない。結局「jQueryを利用しているライブラリやJavaScripコードを読み込むより先にjQuery本体を読み込むこと」を理解していることが大事である。
CSSのレンダリングブロック解除
CSSは<head>
の中から<footer>
や</body>
の直前に移動するだけではレンダリングブロックは解除できない。こちらのブログを参考にCSSを非同期で読み込ませるscriptを </body>
の直前に書いて対処するのがいいよう。
https://takkaaaaa.com/remove-blocking-js-and-css/<script> (function(d){ var s = d.getElementsByTagName('script')[0]; var c1 = d.createElement('link'); c1.rel = 'stylesheet'; c1.href = 'http://ホスト名/CSSファイルを置いてあるパス/style.css'; s.parentNode.insertBefore(c1, s); var c2 = d.createElement('link'); c2.rel = 'stylesheet'; c2.href = '//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'; s.parentNode.insertBefore(c2, s); })(document); </script>
CSSはhead内で読み込むことが推奨されているため、footer内に<link>
タグを記述するのは不適切らしい。なのでfooterにて上記のscriptを走らせ、s.parentNode.insertBefore(c1, s);
でページの最初に読み込まれているscript(基本head内にあるはず)の前に読み込みをさせている。
ただ全てのCSSの読み込みを遅くすると一瞬見た目が酷く崩れて表示されてしまうので、ファーストビューに必要なCSSだけ<head>
の中などに<style>
タグでインライン化して書き出す必要がある。
ちなみにCSSとJavaScriptをインライン化する際はもちろん圧縮(改行やタブ、コメントを削除)する方がよく、これには Pretty Diff という便利なWebサービスがあるのでこちらを利用するのがおすすめです。
使われているCSSが分からない場合
Chrome Developer Tool で要素選択して適用クラスを見ていくしかないかも。「現在のページで使われているCSSの抽出」であれば、同じくChrome Developer ToolのCoverage機能(※)で洗い出せる。
※Toolのタブに見当たらなければ[…]
をクリックしてでるMore Tool?の中に入っています。
WordPressの場合
WordPressの場合、CSSはAutoptimizeプラグインで遅延読み込みすればよい。ただし基本的には読み込み時に一瞬表示崩れが起きるので、その場合はAutoptimizeで最適化&AggregateされたCSSだけがレンダリングブロックした状態になったところで良しとしている。
JavaScriptは Scripts To Footer プラグインが使えたはずだし、functions.phpに遅延読み込みのコードを直接書いてもよい。ただし静的HTMLの場合と同じくjQueryなどの「先に読み込まないとエラーが出るファイル」はレンダリングブロックしてでも先に読み込むしかない。ただしWordPressのwp-includesに入っているjQueryよりも、Google CDN から読み込むようにした方が早くなるようなので、やれる方はやっといた方がいいかもしれません。
// GoogleCDN から jQuery を読み込む
add_action('wp_print_scripts','my_deregister_script',100);
function my_deregister_script() {
if (!is_admin()) {
wp_deregister_script('jquery');
wp_enqueue_script(
'jquery',
'https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js',
array(),
'1.12.4'
);
}
}
適切なサイズの画像
モバイルの場合は画面幅が大きいiPhoneXS Maxでも414px(414×896ポイント)なので、それ以上の大きさの画像はバイト数を無駄に使っていることになります。これを必要最低限のサイズにしてねという警告。
iPhone画面サイズ早見表(図付き) #Swift – Qiita
これも普通にMacでサイズを変更した(iPadも考慮し750pxの横幅とした)上で再度webp形式の画像に変換しアップロードするだけでOKです。
今回の案件ではPCとモバイルでページが分かれていたのでただリサイズして上書きすれば良かったのですが、PC/モバイル兼用のいわゆるレスポンシブなページだとHTMLで画面サイズによって出力する画像を出し分ける書き方があったはずなのでそれを使うか、もしくはCSSの @media
と ‘display や
`background-image` などのプロパティで良しなに出し分けるとかになるのかなと..。肝心のところがなくてすみません..。
こちらのリンクも参考になります。
ファーストビューを優先的に読み込んで、webページの表示速度をアップさせる方法
WordPressの場合
WordPressであればバージョン4.4くらいからレスポンシブイメージと言って画像を挿入したときに画面サイズごとにw300
のような属性が割り当てられたimgタグが自動で用意・挿入されるようになり、基本はこのレスポンシブイメージ機能によりこの項目は自動でクリアされるはず。
しかしテーマによってはこれを無効にしていたり、上手く機能しない場合もあるようで、そう言った時は手動でリサイズするしかなさそう。
キーリクエストのプリロード
これはGoogleの公式ドキュメントが分かりやすいですが、要はjsなどを <link>
で読み込んだ時、それらのコードの中でもjsファイルを読込む必要があると、親のHTML→子のjs→孫のjsという順番で読み込まれて遅いので、「HTMLの<head>の段階で明示的に孫のjsを(preloadを指定して)事前に読み込んでおきましょう」ってことです。
Preload key requests | Lighthouse | Chrome for Developers
ちなみに、色んなブログなどの情報をみて誤解しやすいのは「HTML(親)の<head>
で元からある<link>
に rel="preload"
をつけるんじゃなくて、その<link>
で読んだjsファイル(子)で使うためのjsファイル(孫)を、子から呼び出されるより先に親の段階で以下のように<head>
に読み込みlinkを追記して事前読み込みしましょう」ってことですからね。
<!-- parent.html -->
<head>
<link rel="preload" as="script" href="grandchild-script.js"> <!-- ここを追記しようぜってこと -->
<link as="script" href="child-script.js">
</head>
またフォントの場合は書き方が異なり、以下のように書きます。ちなみにHTMLの記述ルールでは”(ダブルクォーテーション)はあってもなくてもいいみたいです。
<link rel="preload" as="font" type="font/woff2" href="font.woff2" crossorigin>
もろもろの参考はこちらです。
Preload を用いたリソースプリローディングの最適化 | blog.jxck.io
WordPressの場合
jsに関しては静的HTMLと同様にhead内でpreloadを行うだけですが、フォントについてはWordPressではテーマのstyle.cssなどで以下のようにフォントを指定しています。
@font-face {
font-family: "slick";
src: url('/wp-content/themes/sentry-void-master/fonts/slick.eot?1547954276');
src: url('/wp-content/themes/sentry-void-master/fonts/slick.eot?&1547954276#iefix') format("embedded-opentype"), url('/wp-content/themes/sentry-void-master/fonts/slick.woff?1547954276') format("woff"), url('/wp-content/themes/sentry-void-master/fonts/slick.ttf?1547954276') format("truetype"), url('/wp-content/themes/sentry-void-master/fonts/slick.svg?1547954276#slick') format("svg");
font-weight: normal;
font-style: normal;
}
この場合、フォントファミリー「slick」が適用されたHTMLテキストの読み込みが始まるまで上記ソースURLのフォントの読み込みは始まらないので遅くなりますので、以下のようにテーマのheader.phpの<head>
タグの中にコードを追記してやればOKです。静的HTMLと比べて href
のパスがWP独自になっていることに注意。
<link rel="preload" as="font" type="font/woff" href="/wp-content/themes/sentry-void-master/fonts/slick.woff?1547954276" crossorigin>
まとめ
上記で「改善できる項目」に出ていた警告は消えましたが、たまに「サーバーの応答時間が遅い(TTFB)」という項目がでて2秒くらい表示が遅れてると言われたり、他の「診断」などの項目が赤や黄色の警告のままなので、そこを改善しないと90以上のスコアを取るのは難しいようです。
ただしこれが100点になったからといってSEO的にどれだけ上位にきやすいかはなんともで、80点以上でるなら表示速度よりも記事の網羅性・専門性・権威性などの方が重要な気もします。
※2021年からは Core Web Vitals とい指標も追加され、速度だけでなく表示時の画面や本文のカクつきなどのUXもSEOにとって重視されるようになりました。
続き:「診断編」へ
診断編も書きました。診断と言えど「静的なアセットと効率的なキャッシュポリシーの配信」などの項目でスコアは大きく変わるので、こちらもご参考ください。
https://wordpress-life.com/page-speed-insights-renewal-diagnosis/