MiiddlemanサイトでPageSpeed Insightsを改善し40%高速化した話

middleman

Middlemanでできている当サイトのPageSpeed Insightsのスコアを改善し高速したのでやったことをお伝えします。

  1. 静的サイトなのに表示速度が遅くなることもある
  2. 画像を最適化する
  3. ブラウザのキャッシュを活用する
  4. スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/ CSS を排除する
    1. JavaScriptを非同期で読み込む
    2. CSSを非同期で読み込む
  5. 圧縮を有効にする
  6. 結果どうなったか
  7. バージョン情報

静的サイトなのに表示速度が遅くなることもある

サイト作成ツールのMiddlemanを使っていると、静的サイトだからサイト表示のスピードは問題ないだろうと思いがちです。

ですが、実際に速度を測るとあれっというほど時間がかかったり、Googleが提供しているチェックツール PageSpeed Insightsで計測するとひどいスコアだったりします。

本記事ではスコアを改善し高速化した際に行った施策をご紹介します。なお、Middlemanを前提に話していますが、WordPressやMovable Typeなどその他の一般的なサイトを運営されている方にも参考になると思います。

最初はこんな感じでした。

MiiddlemanサイトでPageSpeed Insightsを改善した話

そして事前にChromeの検証のNetworkタブで当サイトのトップページのスピードを測ってみました。

MiiddlemanサイトでPageSpeed Insightsを改善した話

ちなみに、時間に関する数字として、3つ表示されていますが、それぞれ、

Finish 非同期処理も含めて完了にかかった時間
DomContentLoaded HTMLのダウンロードにかかった時間
Load 表示にかかった時間

という意味合いのようです。(こちらのページ参考(英文))

ユーザーが感じるページの表示速度にはLoadが最も適しているようです。4.3秒かかっていることになりますね。

画像を最適化する

MiiddlemanサイトでPageSpeed Insightsを改善した話

まずサイトで使用している画像を圧縮することにしました。画像の大きさ(縦何ピクセル横何ピクセルとか)が大きくなりすぎないようには気を使っていましたが、画質が必要以上に高かったようです。

このあたりのGoogleの考え方については、公式ページ画像を最適化するをご覧ください。

middlemanで画像を圧縮するには、middleman-imageoptimというgemを使います。(gemとはWordPressでいうプラグインのようなものです)

基本的な設定方法は上記ページをみてください。

簡単に使用できるのですが、注意点があります。デフォルトだとjpegの圧縮率が低いです。pngファイルは40〜50%程度圧縮してくれたのですが、jpgは最大で10%程度しか圧縮されませんでした。

なので、ちょっと細工をする必要があります。

まず、jpegoptimというモジュールをインストールします。MacでHomebrewを使っていれば下のコマンドでインストールできます。

1
$ brew install jpegoptim

そして、config.rbを設定します。

最初下のようにしていたのですが、これだとjpgが数パーセントしか圧縮されせんでした。

1
activate :imageoptim

下のようにするとjpgも50〜60%圧縮できました。

1
2
3
4
activate :imageoptim do |options|
  options.allow_lossy = true
  options.jpegoptim = {strip: ['all'], max_quality: 80}
end

allow_lossyとは画像の品質を落とすのを許容するというオプションです。max_qualityはどの程度品質を落とすかということです。100が最高です。

そして、max_qualityが80程度であれば、ほとんど肉眼では差がわからないです。もっと品質が落ちてもよい場合は、この数字を下げることでさらに圧縮率が高まります。

この設定をした状態でmiddleman buildを実行すると自動的に圧縮してくれます。

一番最初のbuildは時間がかかります。当サイトは250記事の程度ですが、2時間かかりました。

ですが、miiddleman-imageoptimの賢いところは、一度圧縮したファイルは管理ファイルに日付とともに記録されてるので、元ファイルに更新があるまで再度圧縮処理をしないところです。なので2回目からは短時間で済みます。

ブラウザのキャッシュを活用する

MiiddlemanサイトでPageSpeed Insightsを改善した話

画像、css、javascriptなどの静的ファイルに有効期限を設定することで、ユーザーのブラウザにキャッシュを残します。すると、次に同じファイルを呼び出そうとした時に、サイトにアクセスするのではなくキャッシュから読み出してくれるようになるので、ネットを経由しない分高速になります。(もちろんサーバー負荷も小さくなります)

サーバーにS3を使っている場合は次のように設定できます。

S3へのアップロード方法に、middleman-s3_syncというgemを使います。

基本的な設定方法は上記サイトを見ていただければと思いますが、config.rbでの設定で注意点があります。

キャッシュに関する設定はs3_syncをactivateする部分より下に書く必要があります。

つまりこのブロックより下に書くということです。ブロックの中ではなく下です。

1
2
3
activate :s3_sync do |s3_sync|
  〜中略〜
end

どのように書くかというと、次のように書きます。

1
2
default_caching_policy max_age: (60 * 60 * 24 * 7)
caching_policy 'text/html; charset=utf-8', cache_control: ''

下のGoogleの公式ページでは、最低1週間以上が望ましいとされているので、60 * 60 * 24 * 7としています。

ブラウザのキャッシュを活用する

default_caching_policyは全てのファイルに適用してしまいます。Googleが求めているのは、画像、Javascript、CSSなどの静的ファイルのみなので、htmlを除外するために2行目を入れています。

なお、middleman-s3_syncのドキュメントには'text/html'とだけ書かれているのですが、実際には'text/html; charset=utf-8'と書かなければ動きませんでした。(middleman-s3_sync (3.3.4)の場合なので、最新版は直っているかもしれません。)

参考(英文): Caching policy for text/html only works if you add ‘; charset=utf-8’ #96

cache_control: ''とすることで、cache_controlに「何も設定しない」ことができます。

この設定をした状態で、middleman s3_syncを実行すれば、有効期限が設定されます。

スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/ CSS を排除する

MiiddlemanサイトでPageSpeed Insightsを改善した話

これは非常にややこしいのですが、要するにJavaScriptやCSSを外部ファイルにして同期的に読み込む処理が、スクロールせずに見える部分(ファーストビュー)の表示を妨げている、ということです。

ちなみに、なぜかここは1ファイルしか表示されないのですが、一つ対処すると別のブロックしているファイルが表示される場合があります。

以下のように読み込んでいるのが、同期的に読み込んでいる状態です。

1
2
<script type="text/javascript" src="javascript.js"></script>
<link rel="stylesheet" href="stylesheets.css">

大抵のHTMLなので入門書には上のように書かれているのが普通なので、このように書く人はすごく多いのではないでしょうか。

コンテンツより後に読み込めばいいんでしょ、<head>内じゃなくて</body>の直前にすれば解決!と思うかもしれませんが、それでは解決しません。

非同期で読み込むようにする必要があります。非同期で読み込むようにすれば、コンテンツの表示を妨げず裏で読み込む処理を行ってくれます。

JavaScriptを非同期で読み込む

ではどうすればいいかというと、公式ページ
レンダリングを妨げる JavaScript を削除する
で説明されています。JavaScriptの場合は簡単で、下のようにasyncを入れるだけでOKです。

1
<script type="text/javascript" src="javascript.js" async></script>

middlemanの場合は、slimなどで次のようにしてあげればOKです。

1
= javascript_include_tag :javascript, async: true

自前のJavaScriptはこの方法でOKなのですが、サードバーティーなどのスクリプトで非同期にしたら動作しなくなるものがりました。

i2iというアクセス解析のスクリプトにasyncを入れたら動かなくなりました。Googleアナリティクスも併用していたので、i2iはやめてGoogleアナリティクス一本にしました。

i2iでアクセス解析できるよりも、ユーザーさんが快適に使えるほうが大事ですからね。

CSSを非同期で読み込む

CSSの場合は少しやっかいです。cssの場合asyncのような便利な機能はありません。JavaScriptを書いて似たような機能を実装する必要があります。

さらに、全てのCSSを非同期に読み込んだ場合、確かに他のコンテンツの表示をブロックはしませんが、CSSが読み込まれるまでの間ファーストビューに全くCSSが適用されない状態になります。なので、そのタイミングは表示が崩れたり、きちんと表示されず、逆に体感速度が遅くなる可能性があります。

なので、できるだけファーストビューで使われるデザインのCSSはHTML内にインラインCSSとして書き込みます。

幸いその方法とコード例は公式ページCSS の配信を最適化するで紹介されています。

なので、現状のCSSを

  • HTMLにインラインで書く部分
  • 非同期で読み込む部分

に分ける必要があります。管理が面倒な方は、後者はフルフルで入ったCSSでも問題ありません。ただ、前者のインラインに書くコードを抽出するさじ加減がちょっと難しいです。なるべくなら、CSSはページをまたがって利用できる外部ファイルに入れておきたいですからね。

なので、ファーストビューを崩さない程度の最小限のCSSを抜き出します。

自分の場合は、ローカル環境(テスト環境)でまず、全CSSをインラインに書いてしまいます。非同期で読み込む部分はコメントアウトしておきます。

そして、ファーストビューが崩れていないか確認しながら、CSSを少しずつ削っていくことで、最小限にすることができます。

あまり、厳密に崩れないかではなく、ざっくりだいたい変化ないくらいでいいと思います。厳密に同じを目指すと、全CSSを書くことになってしまうと思います。背景色、レイアウト、全体の色味などがだいたい同じであれば、ユーザーも違和感を感じないのではないでしょうか。

私がやった感じだと、normalizeやfont-awsomeなどのライブラリー系のCSSや記事内の装飾、フッターのCSSはインラインに入れる必要はなかったので、大分小さくなりました。

slimとscssを利用していてインラインでhead内に書く場合は、下のように外部のscssを呼び出すことができます。

1
2
3
head
  scss:
    @import "before";

非同期にCSSを呼び出すためのコードは先ほど紹介した公式サイトにあるのをほとんどそのまま使ったのですが、一点疑問が浮かびました。

あのコードだと、CSSを呼び出すlinkタグが、head内ではなく、headの上にくるんですよね。なんか気持ち悪いですが、動くのでそのまま使っています。

非同期で読むスクリプトにフルフルのCSSを入れている場合は問題ないですが、分けている場合は順序が変わってしまい、スタイルに影響が出てしまう場合があります。公式ページのコードだと非同期のCSSがインラインのCSSより上にくるようになります。

圧縮を有効にする

MiiddlemanサイトでPageSpeed Insightsを改善した話

画像の圧縮は完了したのですが、テキスト系のファイル(html、css、JavaScript、svgなど)の圧縮をするとさらに速くなります。

middlemanとS3を利用している場合は非常に簡単で、下をconfig.rbに追加するだけです。

1
activate :gzip

バージョンによっては、これだけだとsvgファイルを圧縮してくれないことがあります。その場合は、次のようにします。

1
2
3
activate :gzip do |g|
  g.exts =  %w(.js .css .html .htm .svg)
end

これでmiddleman buildを実行すると、gzip圧縮してくれます。そしてmiddleman s3_syncを実行すると、転送してくれて、S3側のファイルごとのContent-Encodinggzipに設定してくれます。

結果どうなったか

上記の施策を行い、PageSpeed Insightsでチェックしたところ下のような結果になりました。

MiiddlemanサイトでPageSpeed Insightsを改善した話

表示が赤から緑になり、スコアも53から89に向上しました。

ただ、「表示可能コンテンツの優先順位を決定する」というのが新たに出てきました。

これは、おそらく、先ほど実施した「CSSを非同期で読み込む」に関係するものと思われます。ファーストビューの表示がインラインで読み込むCSSでは足りていないということなのでしょう。

もっとインライン側のCSSの量を増やせばおそらくこれもクリアできると思いますが、キリがないし体感的にはファーストビューはしっかり表示されていると感じるので無視することにします。詳しくは下の公式ページをご覧ください。

スクロールせずに見える範囲のコンテンツのサイズを削減する

そして、「ブラウザのキャッシュを活用する」にまだいくつかファイルが表示されています。

ただこれは、グーグルアナリティクスやグーグルアドセンスなどのサードパーティのスクリプトなので、自分にはどう対処すればいいかわかりませんでした。(というか同じグーグルなんだから気にしないでいいようにしてほしい)

MiiddlemanサイトでPageSpeed Insightsを改善した話

  対処前 対処後
Finish 4.47s 13.82s
DomContentLoaded 2.20s 411ms
Load 4.30s 2.62s

Finishが長くなっているのは、JavaScriptやCSSを非同期で裏で読み込むようにしているからだと思います。こちらは特にユーザーの体感速度に関係ないので別に問題ないと思います。

一番重要なLoadが4.30秒から2.62秒に40%速くなったのは大きいですね。体感的にもすごいサクサクしていると感じます。ぜひ他のページを表示して試してみてください。

バージョン情報

ライブラリ名 バージョン
middleman-core 3.4.0
middleman-imageoptim 0.2.1
middleman-s3_sync 3.3.4