見出し画像

本当は速くならない「Webサイトの表示高速化アンチパターン」10選

ベストプラクティスや「高速化につながる!」と紹介されている記事では、逆効果、もしくは効果があるシチュエーションがあまりに限定的な手法が紹介されていることが多いので、アンチパターンとして紹介します。

本記事は「Webパフォーマンス Advent Calendar 2019」2日目の記事です。
https://qiita.com/advent-calendar/2019/web_performance

本記事はWebパフォーマンス高速化の専門家である株式会社Spelldataの竹洞 陽一郎氏にアドバイスをもらいました。HTTP/2の伝送の画像など一部資料のご提供もいただいております。誠にありがとうございます。
https://spelldata.co.jp/

ほとんどの場合で間違い

1. すべての画像をCSSスプライト

その昔、画像をすべて1枚にまとめて、DOMのbackground-imageで位置をずらしながら表示させる高速化手法が流行りました。DOMツリーのパース時に画像を呼び出すimgタグと比べて、画像のダウンロードの開始位置は遅いし、画像自体も大きくなるからダウンロードに時間かかるしで、遅くなります。

昔はサーバが低スペックすぎてコネクションが増える負荷が大きかったのか、画像をひとつにまとめるとブラウザキャッシュがきくメリットの方が大きかったのか、理由はちょっと思い出せないのですが、古の手法です。

なんで保守含めてこんなハイコストなことしてたっけと思ったら、border-radiusがなかったので角丸では画像を4隅に配置しないといけないなどCSSの表現力が当時なかったためではと感想いただきました。そういえば当時svgもなかったですし、base64もブラウザ対応してなかったですものね。

2. 画像の遅延読み込み

DOMLoadedの後にJavaScriptで現在のビューポートを計算してから該当する画像を呼び出すのに速くなりません。

画像3

このように表示領域を上から順に読み込みますので、ビューポート内であっても読み込みの開始は徐々に遅くなります。

画像の遅延読み込みは「表示領域外の画像を読み込まない」ためのプラクティスですので、高速化するというのは間違いです。

画像は、imgで読み込み、decoding="async"でやるのがメインスレッドの処理をブロックしないために最速です。ただ、jQueryなどで画像をまとめて読み込む処理等を行っているとdecoding="async"は効かないので、呼び出し順(Call Tree)を確認するのは大事です。

3. インラインJavaScript

実行が完了するまで、DOMツリーのパースを止めます。「DOM何それこのHTMLファイルではalertメソッドを実行したいんだ!」みたいな特殊な状況以外では速くなりません。

PHPカンファレンス沖縄2019セッション「PHPerでも知っておきたい、フロントエンドでの表示速度最適化手法」でDOMツリーのパースが止まってる実例が示されているのでぜひご覧ください。

https://speakerdeck.com/maepon/phperdemozhi-tuteokitai-hurontoendodefalse-biao-shi-su-du-zui-shi-hua-shou-fa?slide=72

4. HTTP/2への移行

TCPコネクションを1つにまとめるところがボトルネックになります。HTTP/2はTCPコネクション一本の中で複数ファイルの伝送ができることが主軸だったのですが、前処理・後処理のコストが大きく、コネクション複数本で並列処理を行うHTTP/1の方が高速です。

一回の伝送のために前処理を行っているHTTP/2
スクリーンショット 2019-12-10 11.42.55

またパケット損失があった時、飛躍的に遅延要因になります。

最近HTTP/3の仕様が策定されましたが、そこではっきりと「HTTP/2はTCP head-of-line ブロッキングが問題だった」と述べられています。


間違いでない場合もある

5. インラインCSS

HTMLパーサーの計算を遅延させるので、リッチなWebサイトでは、CSSファイルをリンクする方が速い場合が多いです。ブラウザキャッシュもききません。HTMLでのDOMにあたるCSSOM構築が行われるため、その間はDOMのパースも停止します。あと速度と関係ないですが、インラインCSSはカスケーディングの優先度高くなるので保守もしにくくなりますよね。

ただし、CSSはほとんどない、もしくはクリティカル レンダリング パス的にインラインCSSと非同期読み込みを行うCSSを組み合わせると高速化する場合があります。

6. ScriptタグをBodyタグの最後に記述

Scriptタグは非同期の指定がない場合は、DOMツリーのパースを止めます。そのためにdefer/asyncがなかった時代はBodyタグの最後に記述することでDOMツリーのパースの遅延を最小限にするプラクティスが主流でしたが、今ではdefer/asyncをつけてDOMツリーのパース前半(head部分)に持ってくる方がダウンロード開始位置が早いので高速になります。

ただし、jQueryプラグインを利用しているなどによって、defer/asyncをつけることができないコードを同期的に実行したい場合はBodyの最後に記述する必要があります。

7. 画像のインライン化

ファイルサイズの大きい画像のインライン化はHTMLファイルを肥大化させるだけではなく、HTMLのパース計算を遅延させる要因になるので高速化につながらないことが多いです。ブラウザキャッシュも効かなくなります。

ただし、画像がとても軽量でHTMLのパース計算を遅延させない場合に限っては高速化する可能性があります。

8. AMP化

Web Componentsを外部サーバから呼び出しレンダリングする場合遅延します。Server Side Renderingを行った場合、もしくはCDN未使用の上でGoogle検索から表示する場合に限って高速化する可能性があります。


間違いではないけど効果はない

9. 自分の書いたJavaScript/CSSのMinify

改行と空白の削除で得れるファイルの削減は実測では計測できない誤差レベルです。タスクランナーやCIの仕組みをいれてMinifyしているならともかく、Webサービスなどを用いて毎回Minifyするのは手間に見合わず、またコピペミスなども起こりえるのでやめた方がいいでしょう。なお、OSSやCLIでは自動的にMinifyする仕組みを取り入れ、Minifyだけでなくソースコード自体のコンパイルなどと一緒に行っている場合がほとんどです。

10. CSSのショートハンドの利用

高速化手法としてはじめて聞きましたが、1KB1024文字なのでここを気にする前にもっとやることあると思います。Minifyより更に誤差です。


どうしたら速くなるのか

遅延要因は千差万別なのですが、大きく分けるとサーバスペックと内部コードに分類することができます。サーバスペックの最も簡単な方法はお金で解決することです。ちょうど今日同一コードで2つのサーバ構成で簡単に差をとってみたのですが、以下のような結果を得ることができました。

画像2

遅いサーバを利用している場合、CDNを導入するだけで随分変わるはずです。また、それで変わらない場合は配信の問題ではなく、ブラウザのレンダリングの問題である可能性が高いです。以下の記事はわかりやすくブラウザの挙動について紹介しています。

プラクティスを探すのではなく、自分のWebサイトの遅くなってる要因探しをするのが最も速くなるための近道です。ベストプラクティスは血液型占いで結婚相手を探すぐらいの乱暴な話になりがちですので、しっかりと自分のWebサイトと向き合うことが近道です。


終わりに

各所で高速化手法とともに「推測するな。計測せよ。」が語られますが、実際のところ計測はコストがかかるので、ベストプラクティスに沿ったWeb制作が行われている現場がほとんどだと思います。けれど、例えばWebPageTestを使うと簡易計測が行えます。

本来は https://spelldata.co.jp/catchpoint/ などのツールを使ってコストをかけて定常観測すべきですが、それが難しいようでしたらせめてベストプラクティスも、本アンチパターンも鵜呑みにせずに1回でも簡易計算を行ってもらうことができればと思います。

アンチパターンでも高速化につながる特定のケースもありうえます。「alert()だけのScriptを外部ファイルから呼び出し」 vs 「Scriptタグで最後に直書き」ならインラインの方が高速でしょうし、組み合わせや例外はあるので、計測することは重要です。

あと結構この記事書くの大変だったので、Webでモバイルアプリつくる記事もあわせて読んでもらえましたら幸いです。

それでは、また。

この記事が気に入ったらサポートをしてみませんか?