PageSpeed Insights リニューアル後のスピード改善で実施したこと(静的HTMLサイト版)

2018年11月にGoogleのPageSpeed Insights(PSI)が大幅リニューアルされた。LightHouseという今までChrome拡張などのオプションでしかなかったツールが標準導入され、チェック項目や採点の仕方が大きく変わったが、この対策についての情報が少なかったので、今回請け負った静的HTMLサイトの高速化で行なったことを記録に残しておきます。

次世代フォーマットでの画像の配信

jpg, jpeg, pngではなくGoogleが開発した新しいwebp形式の画像を表示するようにすればOK。

このwebp形式自体は数年前からあるが、現在ではChrome以外のブラウザ(Safariなど)では非対応らしい。そのためhtaccessの記述などでjpe?g/pngとwebpを対応非対応で出し分ける必要がある。

コードは「webp htaccess」などでググれば多く出回っている。 https://doocts.com/3288https://qiita.com/miyanaga/items/6570c3c9ae8e15dbb57c

今回の案件ではWordPressではなく静的なHTMLで記述したページの高速化だったので、先のリンクを参考にEWWW OptimizerでWebp形式に変換した画像をダウンロードし、それをサーバーにアップロード、2番目の参考リンクのhtaccessのコードを記述して、PSI上は警告が消えた(本当にChromeなどの対応ブラウザでWebp画像が読み込まれているかはChrome Developer ToolのNetworkタブを開いてページを再読み込みし、typeの列にwebpと表示されるかどうかで確認できる)。

追記

以下のWebサービスでも問題なく画像をwebpに変換してくれ、PSIの警告消えました。 https://lab.syncer.jp/Tool/Webp-Converter/

オフスクリーン画像の遅延読み込み

いわゆるLazyLoadで、一般的にはjQuery依存のLazyLoadライブラリが定番らしいが、調べるなかでjQueryに依存しないlazysizesライブラリの方が軽くて記述も簡単だったので、今回これを採用した。 https://wemo.tech/207

しかしサイト上部で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を後から読み込ませる」もの。

WordPressならAutoptimizeや Scrpit To Footer プラグインなどで対応できるが、今回は静的なHTMLサイトなのでプラグインは使えない。

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>の直前に書いて対処するのがいいよう。

<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>
// 引用元: takkaaaaaの日記

上記ブログによるとs.parentNode.insertBefore(c1, s);でページの最初に読み込まれているscriptの前に読み込みをさせているらしく、これでやるとレンダリングをブロックせずにCSSを読み込んでくれるらしい。

ただ全てのCSSの読み込みを遅くすると一瞬見た目が酷く崩れて表示されてしまうので、ファーストビューに必要なCSSだけ<head>の中などに<style>タグでインライン化して書き出す必要がある。

ちなみにCSSとJavaScriptをインライン化する際はもちろん圧縮(改行やタブ、コメントを削除)する方がよく、これには便利なWebサービスがあるのでこちらを利用すべし。

https://prettydiff.com/?m=beautify

追記

ファーストビューで使われているCSSが分からない場合、Chrome Developer Toolで要素選択して適用クラスを見ていくしかないかも。

「現在のページで使われているCSSの抽出」であれば、同じくChrome Developer ToolのCoverage機能(Toolのタブに見当たらなければ[…]をクリックしてでるMore Tool?の中に入っている)で洗い出せる(このCoverageの使い方も詳しい記事が少なかったのでコメントいただければ書きます)。

追記(WordPressの場合)

WordPressの場合、CSSはAutoptimizeプラグインで遅延読み込みすればよい。ただし基本的には読み込み時に一瞬表示崩れが起きるので、その場合はAutoptimizeで最適化&AggregateされたCSSだけがレンダリングブロックした状態になったところで良しとしている。

JavaScriptは Scripts To Footer プラグインが使えたはずだし、functions.phpに遅延読み込みのコードを直接書いてもよい。ただし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ポイント)なので、それ以上の大きさの画像はバイト数を無駄に使っていることになる。 https://qiita.com/TD3P/items/b3b754bde584abc98f44

これもMacでサイズを変更した(iPadも考慮し750pxの横幅とした)上で再度webp形式の画像に変換しアップロードし、事なきを得た。 こちらのリンクも参考になります。 https://pantograph.co.jp/blog/research/above-the-fold.html

追記(WordPressの場合)

WordPressであればバージョン4.4くらいからレスポンシブイメージと言って画像を挿入したときに画面サイズごとにw300のような属性?が割り当てられたimgタグが自動で用意・挿入されるようになり、基本はこのレスポンシブイメージ機能によりこの項目は自動でクリアされるはず。

しかしテーマによってはこれを無効にしていたり、上手く機能しない場合もあるようで、そう言った時は手動でリサイズするしかなさそう。

しかしこの時はPCとモバイルでページが分かれており、そのうちのモバイル専用ページだったので良かったが、PC/モバイル兼用のページだと414px以上の画像もPCで必要なので、どうやってやればいいんだろう…。@mediadisplayプロパティで出しわけとかするしかないのだろうか。今一度、参考リンクや本で勉強し直す必要がありそうです。

キーリクエストのプリロード

これはGoogleの公式ドキュメントが分かりやすいが、要はjsなどを<link>で読み込んだ時、それらのコードの中でもcssファイルを読込み必要があると、HTML→親のjs→子のcssという順番で読み込まれて遅いので、HTMLの<head>の段階で明示的にpreloadを指定して、子のcssも事前に読み込んでおきましょうってことです。 https://developers.google.com/web/tools/lighthouse/audits/preload

ちなみに、色んなブログなどの情報をみて誤解しやすいのは、HTML(親)の<head>で元からある<link>rel="preload"をつけるんじゃなくて、その<link>で読んだファイル(子)で使うためのcssやjsファイル(孫)を、子から呼び出されるより先に親の段階で、以下のように<head>に記載して事前読み込みしましょうってことですからね。

<linkrel="preload" as="script" href="child-script.js">

またフォントの場合は書き方が異なり、以下のように書くとのこと(ちなみにHTMLの記述ルールでは”(ダブルクォーテーション)はあってもなくてもいいみたいです)。

<link rel="preload" as="font" type="font/woff2" href="font.woff2" crossorigin>

もろもろの参考はこちらです。 https://blog.jxck.io/entries/2016-03-04/preload.html

追記(WordPressでフォントをpreloadする例)

CSSの場合が分かりにくかったので例を追記。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>タグの中に以下のコードを記述しましょうよ」ということですね。

<link rel="preload" as="font" type="font/woff" href="/wp-content/themes/sentry-void-master/fonts/slick.woff?1547954276" crossorigin>

まとめ

上記で「改善できる項目」に出ていた警告は消えましたが、たまに「サーバーの応答時間が遅い(TTFB)」という項目がでて2秒くらい表示が遅れてると言われたり、他の「診断」などの項目が赤や黄色の警告のままなので、そこを改善しないと90以上のスコアを取るのは難しいようです。

続き:「診断編」へ

診断編も書きました。診断と言えど「静的なアセットと効率的なキャッシュポリシーの配信」などの項目でスコアは大きく変わるので、こちらもご参考ください。

 

1+