さっそくですが、皆さん、阿部寛さんのサイトをご存知ですか?
エンジニア界隈の中では、ページ読み込みが早すぎると定番のサイトです。
でも、ほぼHTMLのサイトだし、まあ早いのは当たり前。。
うちのサイトでページ読み込みをここまで早くするのは無理。
なんて思っていませんか?まだ最適化のためにできることがあるかもしれません。
この記事では、阿部寛さんのサイトを目指して、高校生が本気でサイトの読み込み速度を上げるためにやった施策を公開します。
今回最適化したサイト
今回、サイトの読み込み速度を爆速化したのはこちら。
静的サイトジェネレータのJekyllを使用して開発したサイトです。
子ども向けプログラミング学習サイト「メクルン」
https://mekurun.com/
GitHubレポジトリ(良ければFork、Starをぜひ)
https://github.com/Mekurun/mekurun.com
開発&制作メンバー
https://qiita.com/organizations/mekurun/members
GTmetrixでの計測結果
色んな計測サイトがありますが、どれが正確かなどはわからなかったので、
なかでも有名なGTmetrixで両サイトを計測してみました。
※日本のリージョンがなかったので一番近い中国のリージョンで計測
阿部寛さんのサイト
読み込み速度 1.4秒
ページサイズ 40.5KB
リクエスト数 6
メクルン
読み込み速度 1.5秒
ページサイズ 331KB
リクエスト数 24
Fully Loaded Timeという数値がページの読み込み速度なのですが、なんと0.1秒しか変わりません...!
さらに、メクルンは画像やライブラリなども色々と使用しているので、ページサイズは約8倍、リクエスト数も4倍近いです。
もうこれは互角..いや勝った(多分)
やったこと
メクルンの読み込み速度を爆速にするために行った施策をすべて書いてみました。
ここまで最適化するのはけっこう大変ですが、できるところからやってみてもいいかもしれません。
ひとつひとつの細かい説明に関しては省いている部分があります、ご了承ください。
極力ローカルから読み込む
Font Awesomeなど、外部CDNから読み込んでいるスクリプトやスタイル、Webフォントの中で、
ローカルから読み込んでも問題ないものは極力そうするようにしました。
外部CDNは読み込まれるまで時間がかかったり、キャッシュがされなくて速度低下につながったりすることがあります。
JSを非同期で読み込む
使用するJSは基本的に非同期で読み込むようにしました。
asyncやdeferをタグに入れることで対応ブラウザで非同期読み込みが可能です。
<script src="script.js" async></script>
<script src="script.js" defer></script>
asyncとdeferの違いはこちら
https://qiita.com/FeET/items/9445b0518d7e66fa2a26
スクリプト読み込みをFooter部分に移動
後から読み込んでも問題ないJSやCSSなどはbodyの閉じタグの後に読み込むようにしました。
HTMLは上から順番に読み込まれていくので、最後に読み込まれることになります。
画像はLazyLoad
画像は遅延読み込み(LazyLoad)するようにしました。
LazyLoadの対応はimgタグにloading属性を与えることで可能です。
<img src="image.png" loading="auto">
<img src="image.png" loading="lazy">
autoにすると相手の通信状況に応じて読み込むか読み込まないかをブラウザが自動で判断します。
メクルンでは、記事のサムネイルなどファーストビューにないものは遅延ロードするようにlazyにしています。
一部のブラウザには対応していないので、全ブラウザに対応させるためにはライブラリなどで実装する必要があります。
TinyPNGにかける
TinyPNGは画像を圧縮できるサービスです。
ローカルから読み込むFaviconやOGPなどは全てTinyPNGにかけました。
地道な作業ですが、確実に読み込み速度を向上させることができます。
Cloudinaryを使う
メクルンでは、画像の配信をCloudinaryというサービスを使って行っています。
Cloudinaryはサイトで読み込む画像を自動で最適化して配信してくれるサービスです。
Cloudinaryで行ったのは
- 画像圧縮の自動化
- 各ブラウザにあった画像ファイルの配信(WebP,JPEG HR,JPEG 2000など)
- 画像のリサイズ
の3つです。
Cloudinaryは各ブラウザに合わせて画像のファイル形式が最適なものを自動で選んで配信してくれます。
WebPなどにも対応しているので、Google Page Speed Insightsでも怒られることがなくなります。
画像のリサイズや圧縮などもパラメータだけで行うことができるのでとても便利です。
CloudinaryのFetch機能を使えば、画像URLを後ろにつけるだけで利用できるので、こんな感じに設定して使っています。w_{size}で画像のwidth指定、q_autoで画像圧縮を自動設定、f_autoでファイル形式を自動設定、c_fitは比率の設定です。次のようなURLを、imgタグのsrcに入れるだけで簡単に導入できます。
https://res.cloudinary.com/{{UserId}}/image/fetch/w_{size},c_fit,q_auto,f_auto/https://example.com/assets/images/image.png
無料枠があるので、月数万PVを超えなければ十分に無料で使えます。
こちらの招待リンクから登録していただくと、無料枠が増えますので、宜しければ是非。
https://cloudinary.com/invites/lpov9zyyucivvxsnalc5/bjsk4z1eoyrp7mxw16hk
メクルンでは、Jekyllのビルドプラグインでビルド時にパスをCloudinaryのリンクに変換しています。
実装方法については友人の@yuki384が別記事で紹介していますので、ぜひご覧ください。
https://qiita.com/yuki384/items/06ae8a0235bec86e3add
HTTP/2 & gzip配信に対応しているサーバーにデプロイ
HTTP/2とgzip配信に対応しているサーバーを使うことが一番重要です。
詳細に関してはここでは触れませんが、Netlifyはどちらにも対応しているので、
メクルンは、Netlifyにデプロイしています。
Netlify CDN
CDNを使うことも速度向上に大きな効果をもたらします。
メクルンは、Netlifyにデプロイしてホストしています。
NetlifyはAWSのCloudFrontをベースに使った最適化されたCDNを提供しています。
Netlify CDNのパフォーマンスを最大限利用するにはDNS設定でネームサーバーをNetlifyにする必要があるようです。
Cloudflareなどを通してしまうと、逆に速度が遅くなってしまうので注意が必要です。
メクルンでは、Netlify DNSにネームサーバーを移してドメインを適用して運用しています。
Netlifyのデプロイ設定
Netlifyには実はサイトの読み込みを最適化できるオプションがあります。
Netlifyの設定の奥底にこういう設定項目があるのです。
HTMLとCSSのMinify
HTMLとCSSの縮小化を行ってくれます。
余計な改行などを削除して、サイズを減らして配信されるので読み込みが少し早くなります。
Bundle配信
CSSやJSをまとめてHTTPリクエスト数を下げてくれる機能みたいです。
Webpackを使っているので、あまり効果があるのかわからないですが一応ONにしています。
画像の圧縮
Cloudinaryを使っているのであまり意味はないのですが、
これをONにしておくことで、画像の圧縮も行ってくれます。
設定はNetlifyの Settings > Build & deploy > Assets optimization からできます。
preload
普通、スクリプトやスタイルはこのように読み込みますよね?
しかしこれだと、ファイルのリクエストがこのタイミングで走って読み込まれるので少し遅くなります。
<link rel="stylesheet" type="text/css" href="/css/main.css">
先に読み込み(preload)を行っておくと、linkタグが読み込まれた時にすぐに使えるようになります。
CSSファイルやWebフォント、JSファイルなど、必要なものを先に読み込んでおくと読み込み速度が早くなります。
headの開始タグのすぐ下らへんにこんな感じで書くと実装できます。
<link rel="preload" href="/css/main.css" as="style">
対応しているブラウザでのページ読み込みが早くなります。
preconnect dns-prefetch
DNS解決、つまりドメインをIPアドレスに変換する作業がリクエスト時に行われるのですが、
それを先に行っておくことで、scriptや画像CDNからの読み込みの高速化を図っています。
headの開始タグのすぐ下らへんにこんな感じで書くと実装できます。
<link rel="preconnect dns-prefetch" href="https://res.cloudinary.com/">
<link rel="preconnect dns-prefetch" href="https://www.google-analytics.com/">
<link rel="preconnect dns-prefetch" href="https://www.googletagmanager.com/">
Webpack
詳しい説明は省きますが、メクルンではWebpackも使用しています。
WebpackはHTMLやCSS、JSをビルド時に1つのファイルにまとめてくれるものです。
1つのファイルにまとめることで、リクエスト数や読み込みファイルを最小限にして読み込みを早くしています。
PWAに対応
これは読み込み速度にはあまり効果はないかもしれないですが、PWAにも対応しています。
もっと改善できたこと
日本にCDNがあるサーバーを使う
Netlifyが日本にCDNサーバーがなくて、若干遅いのが唯一の欠点。
なので、日本にCDNがあるAWS S3やVercelでホストすればもっと早くなったのかなと思います。
しかし、自動デプロイやフォーム機能が便利なので、Netlifyを使っています。
(はやく無料プランでも日本リージョンを使えるようになってくれぇ..)
Jekyllの代わりにGatsbyやNuxt.jsを使う
今回は、Jekyllで実装を行いましたが、GatsbyやNuxt.jsを使うと、
この記事で紹介した一部の最適化を自動でやってくれるので手早く高速なサイトを作れます。
まだ技術選定されていない方は是非、候補に入れてみてください。
InstantClickを導入する
https://dev.to/ などで使われているInstantClickというライブラリを入れるとページ遷移が爆速になります。
具体的には、マウスを遷移先のリンクに載せるとそのページのファイルなどを先読みしてくれるようなものです。
Google Tag Managerとの関係で、今回は導入を見送りました。
PurgeCSSをつかって使われてないCSSを削除
CSSコードの中には使われていないものも混じっているので、PurgeCSSを使って、
使っていないコードをビルド時に取り除けば、もっとCSSの読み込みがはやくなったのかもしれません。
CSSを別ファイルに分ける
今はサイト内で使われてる全てのCSSコードを同じCSSファイルに書いています。
なのでそのページでは使われていないコードを読み込むことでほんの少し速度が落ちているかもしれません。
それぞれのページでCSSファイルをわけるとより早くなるかもしれないです。
まとめ
いかがだったでしょうか。
以上、高校生が本気でサイトの読み込み速度の最適化に取り組んだ全容でした。
誰かのサイトのパフォーマンス改善に役に立てばと思います。
良ければ記事のシェア、LGTMなどよろしくお願いします。