Webページの高速化/最適化まとめ

おはこんばんちは、渋谷でお掃除してる人です。

#はじめに
自社サービス、ランディングページ、企業ホームページのようなWebサイト（Webページ）において**何が一番大切か**と聞かれれば、その答えは**表示速度**です。

ええ、仰りたいことは大いにわかります。もちろん**コンテンツ**も大事です。
ですが、どれだけコンテンツが良くて魅力的な商品だったとしても、**表示されなければ意味がありません。**

人間という生き物は、Webページが表示されるまで**たったの2秒**しか待てません。
ウルトラマンでさえ3分間も待てるこのご時世に、たったの2秒です。

その2秒によって、汗水流して作りあげたみなさんのコンテンツが人目につくこともなく、消えていくのです。

#というわけで

Webページを高速化する上で大切なことをちょっとだけ紹介します。

この記事を読み終わる頃には、ちょっとだけ高速化に詳しくなって、ちょっとだけ[PageSpeed Insights] (https://developers.google.com/speed/pagespeed/insights/?hl=ja)で高得点を取れるようになるかと思います。

早速やって行きましょう。

#Webページが表示される仕組み
**どうやってWebページが表示されてるの？**って思ったまま読んで頂くのも大変申し訳ないので、ちょっとだけ説明します。

Webページが表示されるまでの大まかな流れは

1. **ブラウザからサーバーへファイルを要求（リクエスト）**
2. **サーバーがブラウザからの要求に応じてファイルを返却（レスポンス）**

たったのこれだけです。（DNSサーバーとかの話は割愛）

上記の流れを超簡単に図にするとこのような感じです。
![Untitled Diagram-Page-3.png](https://qiita-image-store.s3.amazonaws.com/0/192145/5effd807-6082-540a-a2ce-e3e4092979e3.png)

超簡単ですね。


また、リクエストから表示されるまでの流れをもう少しだけ加えると、このような感じです。
![Untitled Diagram-Page-2.png](https://qiita-image-store.s3.amazonaws.com/0/192145/8a018792-133a-d8d4-6adc-5336f468eee5.png)

上記のようにして、僕たち私たちはパソコンのブラウザ上でWebページを見ることができています。
実際は表示するまでに何度もリクエストを飛ばしているわけですが、その辺はマサカリ飛ばさないでほしいです

## Webページの高速化
では、**Webページをどのように高速化してくのか**という話に移りましょう。
高速化していくためには、**最適化**をする必要があります。
英語でいうとOptimizationです。テストに出るので覚えましょう。

まさに最適化をすることで、Webページは高速で表示され、閲覧する人全てがハッピーになります。
そこで、今回は**ネットワーク処理**と**描画処理**の2つに分けて見て行きます。

### 最適化のポイント
具体的な最適化の方法を紹介する前に、**ネットワーク処理/描画処理とは？**っていう心の声が聞こえてしまったのでちょっとだけ補足しておきます。

#### ネットワーク処理

今回は便宜上、**描画処理以外で色々やってること**っていうことにしておきます。
深くは考えないようにしてください。私は深く考えられないタイプの人間なので。

#### 描画処理

Webサーバーにリクエストを投げて、受け取ったレスポンスから**Webページを描画する処理**です。
Webページが表示されるまでに、以下の工程を踏んでいます。

1. DOM/CSSOMツリーの構築
2. DOM/CSSOMツリーからレンダーツリーの構築
3. レイアウトの決定
4. ペイント(描画)

上記のように**レンダリング処理**などを行い、そして、JavaScriptによってDOMツリーに**あーだこーだ**と**スクリプト処理**が行われます。

#最適化の方法
前置きが長くなってしまいましたが本題に行きましょう。

ネットワーク処理の最適化の方法は以下の３つを紹介します。

- **リソースの最適化**
- **リクエスト数の最適化**
- **クリティカルレンダリングパスの最適化**

また、描画処理の最適化の方法は以下の2つを紹介します。

- **レイアウト計算/スクリプト処理の最適化**
- **メモリの最適化**

### リソースの最適化

![Untitled Diagram-Page-3 (3).png](https://qiita-image-store.s3.amazonaws.com/0/192145/0aae4bfb-fca7-267a-071d-b847be7651f3.png)


#####JS/CSS/Imageの最適化

JavaScriptとCSSに関してはMinifyします。ミニファイです。
`.min.js`という拡張子を見たことがありませんか？
これこそがまさにJS/CSSの最適化です。

通常は、GulpのようなタスクランナーやWebpackのようなモジュールバンドラーを使って最適化をします。
画像ファイルに関しては、リサイズしたりすることによって大幅にファイルサイズを削減することができます。Gulpで言うところの`gulp-imagemin`などですね。

ちなみに、`.min`っていう拡張子は「Minifyしたぞ！」っていう意思表示のためだけにつけられています。

#####圧縮

圧縮アルゴリズムの種類として`gzip`, `zopfli`, `brotli`などがあり、目新しいのはGoogleの圧縮アルゴリズムである`brotli`です。 
圧縮については最適化と違って以下の2種類のルートがあります。

1. 予め圧縮したファイルをサーバーに置く
2. サーバーで圧縮する

`1.予め圧縮したファイルをサーバーに置く`に関しては、上記のGulpやWebpackなどを使って予め圧縮する方法です。
`2. サーバーで圧縮する`に関しては、`Apache`の`httpd.conf`に`mod_brotli`や`mod_deflate`の設定を書き、それぞれモジュールを置いておくだけでサーバー側で圧縮して返却するようになっています。

`brotli`に関しては、導入するために`httpd`をソースから落としてきたりする必要があるようで、導入コストがやや高めとなっています。
ケースバイケースですが、最低限`gzip`に対応しておく必要はあると思います。
サーバーで圧縮する場合は、圧縮するだけサーバーに負荷がかかるため、可能な限り予め圧縮しておきましょう。

### リクエスト数の最適化

#####画像をまとめる

いわゆるCSSスプライトという技術で、表示する画像を1つのファイルにまとめる方法。
表示する画像ファイルの分だけリクエストを送るため、それらを1つにまとめることによって1回のリクエストで済ませることができます。

#####JS/CSSをまとめる

コーディングをする上ではそれぞれ別ファイルで管理していた方が進めやすいですが、別々のファイルとして読み込んでいると、それだけリクエストが多くなるので、なるべくJSとCSSをそれぞれ1つのファイルにまとめた方が良いです。

#####キャッシュする

`httpd.conf`等に`mod_expires`の設定を書くことによって、キャッシュのルールを設定することができます。

```httpd.conf
<ifModule mod_expires.c>
     ExpiresActive On
     ExpiresByType text/html "access plus 1 months"
     ExpiresByType image/jpeg "access plus 1 months"
</ifModule>
```

上記の設定によって、HTMLファイルと画像ファイルをそれぞれ1ヶ月間の期限を設定してキャッシュするようになります。
頻繁にリニューアルされるページや更新されるファイルについてはあまりオススメできませんが、ファイル名をバージョニングをすることによって強制的にキャッシュを効かなくする方法もあります。

期間に関しては、GoogleDevelopersやRFCのガイドラインにも書いてある通り、１週間〜1年間の間で設定すると良いです。
また、キャッシュそのものについては[Cache-controlヘッダー](https://developer.mozilla.org/ja/docs/Web/HTTP/Caching)で設定することができます。

Cache-controlについて、最近追加された[immutableレスポンス](https://tools.ietf.org/html/rfc8246)もありますが、現在は対応するブラウザが少ないため、割愛します。

### クリティカルレンダリングパスの最適化

HTMLファイルの`body`タグの内容が表示されるまでにブラウザはサーバーにリクエストをめちゃめちゃ投げます。
例えば`head`タグ内でCSSファイルやJSファイルをたくさん読み込んでいると、その分だけ表示されるまでの時間が伸びます。
そういった**レンダリングブロック**の解決策は、**ファーストビューが綺麗に表示される最小限のファイルだけ読み込む**ことです。

基本的に、JSに関しては`body`の閉じタグ直前に置くことによって解決しますが、CSSに関してはそう簡単には行きません。
単純にCSSの読み込みを遅くした場合、一瞬ですがCSSが適用されていないページが表示されてしまいます。

ですので、[Critical Path CSS Generator](https://jonassebastianohlsson.com/criticalpathcssgenerator/)のようなサイトで、ファーストビューに使われるCSSのみ抽出し、`ファーストビュー用のCSS`と`全てのCSS`の2つにCSSファイルを分けます。（あるいはファーストビューのCSSのみ直接インライン化します）

### レイアウト計算/スクリプト処理の最適化
![Untitled Diagram-Page-3 (2).png](https://qiita-image-store.s3.amazonaws.com/0/192145/8058802f-b74d-52db-35fc-b799f74987f3.png)


#####ルールセットやCSSセレクタを整理する

Webページを更新していると、使われなくなったCSSが生まれます。そういったCSSの記述を定期的にメンテナンスしていないと、それだけ処理の無駄な時間が増えてしまいます。

また、`.class .div .div a`などとCSSセレクタを記述した場合、それだけマッチングの時間がかかってしまいます。
ですので、`class`や`id`を適切に振り、なるべくマッチングの処理時間を減らした方が良いです。

#####CSSとJSのアニメーション

基本的にCSSで実現できるアニメーションはJavaScriptでも実現することができます。

ですので、JavaScriptでアニメーションを書いてしまいがちですが、WebWorkerなどを使わない限りJavaScriptはメインスレッドで処理が行われるので、他の処理にも影響を与えてしまいます。
一方で、CSSのアニメーションはコンポジットスレッドで処理されるためメインスレッドに影響を与えることはありません。

可能な限りCSSで実現することによってサクサクのWebページにすることができます。

#####処理の時間を計測する

ChromeのデベロッパーツールのPerformanceパネルで計測することによって**どの処理にどれだけの時間を費やしたか**確認することができます。
<img width="773" alt="スクリーンショット 2018-09-16 16.34.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/a9aef6f8-e933-e12a-b399-3b6183667b5f.png">

範囲を指定すると、以下のようにミリ秒単位で処理時間の内訳を表示してくれます。
<img width="570" alt="スクリーンショット 2018-09-16 16.36.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/92a9ac38-35ce-c61c-c563-c17c53200e16.png">

処理が重い原因を見つける際にはこういったプロファイラを使って地道に見つけて行きましょう。
この作業でほとんどの重い処理は解決できます。

### メモリの最適化
メモリに関しても、ChromeのデベロッパーツールのPerformanceパネルで確認することができます。

以下の画像の赤の枠内がメモリ使用量の推移を表しています。
<img width="984" alt="スクリーンショット 2018-09-16 16.53.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/48c13f2f-c888-c437-d3ea-f9bd32170d03.png">

上記の画像の場合は処理が終わった後に使用量がガクッと下がっていて異常はありませんが、解放されないメモリが増え続けて、ヒープが圧迫される場合は使用量が上がりっ放しになります。
こうなってくると、ガベージコレクションの実行頻度も上がり、負のスパイラルに陥ります。

Performanceパネルでメモリリークが発生するおおよそのタイミングを特定し、Memoryパネルのスナップショットで参照が残ってしまっているオブジェクトなどを特定しましょう。

#スピードテスト
Webページの高速化のようなパフォーマンスチューニングにおいて、とても素晴らしい名言があります。

それは、**「推測するな、計測せよ」**です。

**「許可を求めるな、謝罪せよ」**に並んで、**全日本エンジニアが知るべき大事な考え方選手権の上位に食い込む名言**だと思っています。

というわけで、いくつかスピードテストするためのサイトを紹介します。
検証のために[有名なサイト](http://abehiroshi.la.coocan.jp/)で測ってみました。

###[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/?hl=ja)

有名どころですね。
Googleが提供するページスピードを測るツールです。


<img width="447" alt="スクリーンショット 2018-09-16 17.15.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/96407c48-2718-a4e9-15c2-fb65b5cfce6f.png">

点数の他にも、最適化の方法を教えてくれます。


###[WEBPAGETEST](https://www.webpagetest.org/)
個人的にはココのサイトが好きです。
割と厳しめに評価してくれて、かつそれぞれの評価基準を段階評価してくれます。

<img width="477" alt="スクリーンショット 2018-09-16 17.16.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/736e1ad5-6f7c-eda2-dfaa-12bef9b00800.png">

PageSpeed Insightsでブイブイいってた[有名なサイト](http://abehiroshi.la.coocan.jp/)もここでは厳しめに評価されています。


###Chrome Developer Tool
**いやいや、ページ飛ぶのめんどくさいよ**って人向けに、Chromeのデベロッパーツールにも搭載されています。

<img width="785" alt="スクリーンショット 2018-09-16 17.17.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/192145/14bae8b6-6817-4e47-23f2-e4d5b4156d24.png">

手軽に計測できてUIもシンプルで見やすいので、そこそこオススメです。

#最後に
パフォーマンスチューニングはとても奥が深いので他にも色々な方法があります。
CDNやサーバースペックなどに関しては触れませんでしたが、フロント・バック・インフラ全ての人に関わる部分なので、覚えておいて損はしません。

ちなみに、ブラウザの表示までの細かな動きは[実際のところ「ブラウザを立ち上げてページが表示されるまで」には何が起きるのか](https://qiita.com/tsin1rou/items/d4c781a2f25e2b92fa5e)に詳しく書かれています。とても勉強になりました。