thumbnail

こんにちは。Lostmortal (@lostmortalmusic) です。

本業はフロントエンドエンジニアです。勤め先での資料として作成したものですが不要になったため一部を公開します。WEB サイトの高速化についてのガチめな内容になります。

フロントエンドにおける高速なWEBページとは

フロントエンドにおける高速な WEB ページの定義とは、概ね以下の要件を満たすものであるといえます。

  • ページそのものや CSS など周辺ファイルの読み込む容量が少ない
  • 処理や処理による負荷が少ない
  • 適切な順序で処理が行われている

高速なサイトの事例としては、一部界隈では有名ですが、阿部寛さんのホームページはとても速いです。

http://abehiroshi.la.coocan.jp/

ただ、このケースの場合は、WEB サイトの要件が複雑になる以前の時代の作りのために容量が非常に少なく、処理も非常に少ないことが速さの理由です。

一方、現代の WEB サイトの要件では上記のように極端にシンプルなサイトを作ることは現実的でなく、読み込む容量も処理も、どうしても増えてしまいます。

そこで、出来る限りの軽量化をしたり、擬似的に容量や処理を少なくすることで高速表示を実現する。

その方法を以下に列挙していきます。専門的な話になりますので何卒。

初級

【画像】画像のファイルサイズを小さくする

WEB ページのファイルサイズで最も大きなものは画像である場合が殆どのため、画像のファイルサイズを絞ることが軽量化の第一歩と言えます。

画像は最適化(圧縮)することで、ピクセルサイズを変えずにファイルサイズを縮小できますので、まずはこれを試したいところ。

画像のファイル形式及び圧縮形式について

画像のファイル形式及び圧縮形式について代表的なものは以下。

JPEG (.jpg)(ジェイペグ)

  • フルカラーを扱える形式で、写真などの扱いに優れている。
  • 圧縮率を上げると画質が劣化する
  • 一度劣化したものは元に戻せない(不可逆圧縮)
  • 圧縮率を上げ(品質を下げ)て書き出しを繰り返しているとボロボロになるので注意

PNG (.png)(ピング)

  • フルカラーを扱えて、品質を戻せる可逆圧縮
  • フルカラー写真は JPEG より重くなる。
  • WEB デザインのパーツやベクターイラストなど写真ほど色数が多くない画像に適している。
  • 背景透過が可能。
  • PNG8 という256色までの圧縮形式を用いれば更に軽くなる。

GIF (.gif)(ジフ)

  • 可逆圧縮と背景透過が可能
  • 使える色数は256色まで
  • アニメーションが使える
  • 純粋な256色縛りフォーマットとしては PNG8 の方がやや優秀

WebP (.webp)(ウェッピー)

  • Googleが開発した比較的新しい形式
  • この中で最も軽量&有能
  • 可逆圧縮と非可逆圧縮が選べて背景透過もアニメーションも可能
  • JPEG や PNG と、それぞれ同じ圧縮形式と画質で比較して3割くらい軽量化が可能
  • safariと IE が対応していないので、使う場合はこれらを考慮したフォールバック処理が必要
Safari は Safari 14 から WebP に対応することが発表されました。やっと重い腰を上げてくれました。

画像最適化(圧縮)の方法

ツールを使えば一発です。

WordPress の場合は EWWW Image Optimizer などのプラグインを入れておけば自動でやってくれます。

自分で手動でやるなら、kraken.io などのブラウザサービスが便利です。

【画像】【HTML】レスポンシブイメージを使用する

<picture> や srcset を用いて、デバイスごとに最適な大きさの画像をロードする事が可能になります。

これまで様々なデバイスに対応させるために大きめの画像を用いていた部分でも、無闇に大きな画像を読み込む必要がなくなります。

画像ファイルのサイズ展開が必要にはなりますが、本来そのデバイスに必要ない大きな画像を読んで容量を喰う状態を防ぐことが出来ます。

<img srcset="images/small.jpg 320w,
             images/medium.jpg 640w,
             images/large.jpg 1280w"
     src="images/large.jpg" alt="hoge">

【CSS】【JS】ファイルを圧縮する

CSS や JS ファイルについて、改行や空白を取り除いてファイルサイズを圧縮する方法(minified)

minify されたファイルを直接編集するのは非現実的なので、作業用の展開(expanded)ファイルと分けて管理をします。

対応方法

ツールを使う。環境構築をして自動化するか、手動でやるなら下記サイトなどで対応可能。

https://www.minifier.org/

【HTML】JSの読込をフッターに移動する

ページに読み込む JS ファイルの記述を <head> 内に書いている場合、可能なら(※) </body> 直前などページの下部に移動したほうが良いです。

(※DOM のロードよりも先に処理を走らせる必要があるような特殊なケースを除いて、殆どの場合は可能です)

ブラウザは HTML の記述を上から順番に読み込んでいくため、 <head> タグに JS があると、その JS の読込が終わるまで、以降の記述が読めなくなってしまいます。(レンダリングブロック)

非同期処理(後述)という対策方法もありますが、通常の読み込みを使用する場合は記述をページ下部に移動することでこれを回避できます。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>タイトル</title>
<script src="hoge.js"></script>
</head>
<body>
    内容
</body>
</html>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>タイトル</title>
</head>
<body>
    内容
    <script src="hoge.js"></script>
</body>
</html>

【JS】【CSS】外部のライブラリ(jQuery等)をCDNから読み込む

jQuery などの外部ライブラリは、CDN から配信されているものが多いです。(CDNについては後述)

このようなファイルは、自サーバーに置かず、CDN から高速配信されているファイルを読み込むことで、サーバーから読み込む容量を節約することが出来ます。

<script src="./asset/js/jquery-3.4.1.min.js"></script>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

中級

【画像】遅延読み込み (lazyload) する

スクロールしないと見えない画像や iframe について、ページの初期表示段階では読み込まずに、スクロールして表示される段階で読み込むようにする対応です。

高速化施策として割と一般的になりつつある方法かもしれません。スクロール量と画像が多いページには絶大な効果ありです。

対応方法

WordPress ならプラグインで対応可能です。

自分で外部のライブラリを読み込むなら、lazysizes がおすすめです。

↓ここが詳しいです。
https://oxynotes.com/?p=10810

Chrome では、<img loading=”lazy” と記述するだけで対応してくれるようになりました。他のブラウザでも実装が進んでいるので将来的にはこれがスタンダードになりそう。

【CSS】レンダリングブロックの回避

<head> タグ内で読み込まれている CSS は、そのファイルのロードが終わるまで、以降の HTML データの解析を止めてしまいます。

これをレンダリングブロックといいます。

しかし、JS と同じように CSS をページ下で読んでしまうと、一度スタイルが当たっていないデザイン崩れ状態が表示されてから、カクッとなってデザインが反映されるという挙動になります。

これは UX 上よろしくないですよね。

そこで、CSS によるレンダリングブロックを回避する方法として、

  • ファーストビューに表示される要素のみ <head> 内にインラインで記述
  • 残りの記述をページ下部で読み込む

という方法が推奨されています。

管理性の問題や、ファーストビューの表示領域はデバイスによって異なることから、完全な対応をするのは難易度が高めですが、全てインラインで出してしまうというのもあり。(当サイトも全てインラインで CSS を読んでいます)

Critical-Path CSS という概念もありますが専門性が更にブチ上がるので今回は割愛。

【CSS】セレクタの親子関係を減らす

親子セレクタ( div > p など)が増えると CSS の解析が重くなっていきます。

BEM 記法などを駆使して、極力セレクタに親子関係を持たせずにスタイルを当てていくと良いです。

BEM を使った例

div.hoge {
    padding: 10px;
}
div.hoge > p {
    font-size: 14px;
}

.hoge {
    padding: 10px;
}
.hoge__txt {
    font-size: 14px;
}

【JS】処理のスコープを絞る

JS の処理の対象を id で指定する、親要素を予め指定するなどしてスコープを絞り込むように記述すると、要素検索の負荷を減らすことが出来ます。

処理や対象が多くなるほど効果は大きくなります。

例:「あああああ」をクリックしたら文字色が赤くなる処理(jQuery)

$('.js-fuga').on('click', function(){
    $(this).css('color', 'red');
});

$('#js-hoge').on('click', '.js-fuga', function(){
    $(this).css('color', 'red');
});

【HTML】【CSS】簡単な動的処理にはJSを使わない

  • アコーディオン
  • スライドメニュー
  • カルーセル
  • モーダル
  • スムーススクロール

など、この辺りの簡単な動的要素は、要件次第では JS を使わずに HTML, CSS のみで実装が可能です。

JS の読み込みも処理もカットできるため実現できればそれなりに軽量化が可能になります。

【HTML】【JS】JSを非同期で読み込む

ブラウザが HTML の記述を上から順番に読んでいくのは前述のとおりだが、JSをこの流れ(HTMLパース)とは別軸で処理させることが出来ます。

これを非同期処理といいます。

やることとしては、 <script> タグに async 又は defer と記述します。

<script async src="hoge.js"></script>
<script defer src="fuga.js"></script>

async と defer の違い

async は、JSファイルのダウンロード及び実行がそれぞれHTMLのパースと別軸で行われます。(完全非同期)

defer は、ダウンロードが非同期で行われ、実行はHTMLパース後、記述順に行われます。

よって async の方がよりパフォーマンスを出しやすいですが、複数のJSファイルに依存関係があり、記述順が変わると処理がうまく走らない場合 (jQuery など) は defer を使います。

【CSS】【JS】【画像】CDNを利用する

CDN (Contents Delivery Network) とは、WEB コンテンツを配信するために最適化されたネットワーク(データ保管場所)のことです。

画像などのアセットを CDN サーバーにキャッシュさせておくことで、それらのファイルを自サーバーからロードする必要がなくなり、読み込み容量を削減できます。

外部ライブラリなら読み込みパスを変えるだけで対応できるが、自ファイルを CDN に対応させるには色々な設定が必要になります。

当サイトでも、Cloudflare という CDN を利用しています。

【HTML】DOMツリーを深くしすぎない

HTML の DOM 構造を極力シンプルに構成することでブラウザレンダリングにおけるデータ解析の負荷を減らします。

CSS と連携して効率良いコーディングを目指す。実装者の腕が試されるところですね。

上級

AMPを導入する

AMP というモバイルページを爆速化する規格があります。

詳しくはまた別の機会に。

PWAを導入する

PWA (Progressive Web Apps) を導入し、Service Worker のキャッシュ機能を活用することによって高速化を実現できます。

詳しくはまた別の機会に。

【CSS】CSSカバレッジの最適化

ページ内に存在している要素に対応する、必要な CSS だけが読み込まれている状態に近づけることを CSS カバレッジの最適化といいます。

CSS ファイル内に、ページで使用されていないセレクタ等の無駄な記述があると以下の理由でパフォーマンスが下がります。

  • 余計な記述の分のファイル容量増加
  • CSSOM と DOM のマッチング時に、無い要素への要素検索が走ってしまう(不要な処理が増える)

様々な要件がある中で完全に実現する難易度は非常に高いです。

殆どの WordPress テーマなどではこの辺はボロボロだと思いますが、自分で CSS 設計をする際は使用されない記述を極力書かないように工夫した方が良いです。

【CSS】アニメーション操作のプロパティに気を使う

ページロード後、アコーディオンやカルーセルなどでアニメーションが発生する場合の話になります。

アニメーションの度にブラウザレンダリングの処理が発生しますが、この処理を少しでも減らしたいよねという話です。

ブラウザレンダリングは、(中略)Layout → Paint → Composite というフローで処理されますが、アニメーションの記述の仕方によって、Layout の工程から再計算になるか、Paint の工程から再計算になるかが分かれます。

当然、Layout の工程から再計算になるような処理を避けたほうが速くなるわけです。

具体的な記述方法の例としては、メニューの開閉を left や top ではなく、transform で操作するなど。

詳しく書かれているエントリーがありました↓

https://qiita.com/codemafia0000/items/caed57ec30d638e40728

その他

筆者がフロントエンドエンジニアなので、ここまでフロントエンドにフォーカスした内容を掲載してきました。

以下はその範疇から外れるものの、こんな事もできるよというものです。

リストでサクッと紹介します。

  • キャッシュの設定
  • HTML の圧縮
  • Gzip や Brotli による圧縮
  • 設計段階から効率よく作る
  • サーバーのスペックを上げる

非推奨の施策

以下は基本的にあまりおすすめ出来ない方法です。

CSSスプライト

UI に使用する画像を1つのファイルにまとめて、読み込みファイル数を削減する方法。

現在は http/2 技術の登場によって、複数ファイルを同時にロードできるので高速化観点では無意味になったといえます。

ただ、http/2 に対応していないサーバーでは引き続き有効なのと、高速化以外でのシチュエーションで有効な場合もあります。

画像埋め込み

画像を Base64 という形式に変換して、HTML や CSS に直接埋め込む方法。

こちらもリクエスト数を減らせるメリットがありましたが、http/2 の登場により不要になりました。

Base64 化自体はファイルサイズが大きくなる手法のため、現在は余程事情がない限り非推奨といえます。

display: none によるレスポンシブ対応

HTML に PC 用とスマホ用の記述を別々に書いておいて、CSS の display: none で表示非表示を切り替える対応方法。

初心者コーディングあるある的な手法で、実際に時々見かけます。

display: none で非表示にしても要素自体はロードされてしまうので、この手法では各デバイスで見えていない要素に容量を喰われることになります。

もしこうなっている場合は、設計や要件定義の段階から見直すべきかも知れません。

プラグイン爆盛り(Wordpress やその他 CMS)

Wordress などの CMS を利用する場合、高速化を実現するプラグインを利用出来るケースがあります。

調べると結構色々出てきて、色々な施策をプラグインで実現可能です。なので色々と導入したくなってくるかも知れません。

しかし、プラグインの追加=処理の追加であるため、プラグインを入れること自体が重くなる可能性を持ち合わせています。

高速化用途であっても、あまりプラグイン数が増えてしまうと、逆に重くなる可能性があるので注意が必要です。

合わせてどうぞ