サイトの表示高速化につながる18のこと

124,220View
酒井琢郎のプロフィール画像
エンジニア酒井琢郎

ベイジでエンジニアをやっている酒井です。

ベイジには2017年に、新卒で入社しました。いつもはJavaScriptの開発からWordPressのカスタマイズなど、フロントエンドを中心としながら、一部バックエンドも含めて仕事をしています。『knowledge / baigie』でも、フロントエンド寄りの情報を発信していきたいと思います。

私の今日のテーマは、表示の高速化についてです。

webサイトの表示スピードは、webサイトのユーザー体験に直結すると私は考えています。表層的なUIデザインの改善以上に重要かつ効果的であることも多いため、webのデザイナーやエンジニアは、0.1秒でも速くなることにこだわるべきでしょう。

表示高速化の手法というと、サーバ側の話になることも多いですが、実はフロントエンド側でできることもたくさんあります。それは大きく3つに分けられます。

  • ブラウザ処理の高速化
  • 通信の高速化
  • 体感速度の高速化

一つ目の「ブラウザ処理の高速化」は、ブラウザがソースコードを解析する仕組みを理解し、描画までの時間をできるだけ短くして表示時間を短くするような手法です。

二つ目の「通信の高速化」は、ファイルの軽量化や通信方法の変えることで、通信時間を短くして表示を速くするような手法です。

最後の「体感速度の高速化」は、処理や通信を実際に速くするわけではないが、ユーザーには処理が速いと錯覚させるような手法です。

この3つの観点を踏まえたうえで、私が実践している、あるいはこれからやろうとしている、表示高速化に繋がる具体的な手法やポイントをまとめてみました。

1. 外部ファイル化

CSSやJavaScriptの外部ファイル化は、ファイル管理やメンテナンス性などの面から広く行われていますが、「通信の高速化」という観点でも有効です。

一般的に、変更の可能性が高いHTMLはキャッシュさせないことの方が多いと思いますが、その場合にCSSやJavaScriptをHTMLにインラインで直書きしていると、リロードするたびにHTMLと一緒にこれらのソースコードも再読み込みします。つまりその分だけHTMLのファイルサイズが大きくなり、通信量が増えてしまいます。

一方、CSSやJavaScriptのファイルを外部化し、それらをキャッシュ化するようにしておけば、リロード時の通信量がその分削減されます。このような状況を想定すると、基本的には、CSSやJavaScriptは外部ファイル化したおいた方が、表示の高速化には繋がりやすいというわけです。

ただし、外部ファイル化が、必ず表示高速化に繋がるというわけでもありません。

ブラウザがwebページを表示する時、HTMLコードを上から順に文法に則っているかチェックしながら解析する「HTMLパース」という処理を行います。この解析処理は、<script>タグなど外部ファイルを読み込む箇所に到達すると、解析を一旦停止し、外部ファイルのダウンロードと、外部ファイル内に記述された処理の実行を優先します。

このような処理の流れがあるため、外部ファイルが存在すると、それを読み込み、処理する時間の分だけ、HTMLに書かれた情報の表示が後ろ倒しになります。つまり、初回読み込み時などのキャッシュが効かない時には、CSSやJavaScriptを外部ファイル化した方が表示が遅れる、ということも起こりえます。

例えば、初回訪問者が多いホームに重い処理を仕込みたい場合には、外部ファイル化よりも、ファーストビューの範囲で用いられるCSSやJavaScriptだけ<head>内にインラインで記述した方が、外部ファイルの通信を挟まずに表示が始まる分、ファーストビューの表示が速くなります。

運用面から見てもCSSやJavaScriptは外部ファイル化されている方が基本的には有利ですが、部分的に高速化を図りたい場合には、外部ファイルに記述していた処理をあえてインラインに記述する、という判断がなされる場合もあります。

2. 読み込み時の記述順

表示の速さをユーザーに体感してもらうためには、<head>内でCSSやJavaScriptの外部ファイル読み込む時の記述順にも注意しましょう。

例えば先に<script>タグでJavaScriptファイル、その下の行に<link>タグでCSSファイルを読み込むような順番で記述すると、ブラウザとしては、JavaScriptファイルの読み込みに到達した時点でHTMLパースを一旦停止し、JavaScriptの解析、あるいは処理が終わった後に、CSSを読み込み、解析し、適用します。そのため、CSSの適応までにタイムラグが生じ、素のHTML要素が一瞬表示されたりするなど、デザイナーが意図したページの表示が遅延する可能性が出てきます。

これを回避するために、CSS→JavaScriptの順序で記述します。後述するように読み込みや処理の実行をコントロールできる属性も存在するため、この記述順を守るのが必須ではありませんが、特に意図が無ければ、CSS→JavaScriptの順番で記述することをデフォルトにしておいた方が良いでしょう。

3. defer属性・async属性の付与

HTML5では「defer」や「async」という属性を使うことができます。例えば、<script>タグにdeferを付与して記述すれば、JavaScriptの読み込みをHTMLパースを止めずに行い、さらに読み込んだJavaScriptの実行を後回しにし、表示を速めることができます。

<head>内に<script>タグを記述すると、「DOM要素が存在しない」といったエラーがしばしば起こります。これはHTMLパースが完了する前にJavaScriptを実行してしまうことで、DOM要素を見つけられなくなる現象であり、HTMLやJavaScriptの構文が正しくても起こりえます。しかしこれも、defer属性を付与すれば回避できます。

なお、deferもasyncもHTMLパースを止めずにJavaScriptのダウンロードを行える点では同じですが、deferが、HTMLパースが終わるまでJavaScriptを実行しないのに対して、asyncは、JavaScriptのダウンロードが終わり次第、JavaScriptを実行し、その間はHTMLパースが止まります。

表示に影響を与えるようなJavaScriptを読み込む場合には、asyncを用いた方が表示が速くなる場合もありますが、どちらかといえば、deferを用いた方が表示が速くなることの方が多いでしょう。

4. CSSの非同期化

JavaScriptにおけるdefer属性のようなものは、CSSでは用意されていませんが、別方法で、CSSも非同期にし、HTMLパースをコントロールすることができます。

正攻法で行けば、<link>タグの属性rel="stylesheet"の値を"preload"にすればCSSファイルの読み込みを非同期にできます。しかしこの方法では、ブラウザのサポート状況がバラバラであり、完全には対応できません。そこで採用したいのが、<link>タグのmedia属性を書き換える方法です。

    <link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

    この方法だと、media属性に"print"を指定します。このように定義すると、印刷用のスタイルシートと解釈され、HTMLパース完了後に読み込まれるようになります。それをonloadに記述したJavaScriptによって、読み込み後にmedia属性を"all"に変更するのです。こうすることでHTMLパース完了後にCSSの読み込みが行われるようになります。ファーストビューの表示に関与しないCSSのデータ量が大きい場合は、このようにして表示速度を速めることも可能です。

    5. 外部ファイルの結合

    メンテナンスやチーム作業の観点から、CSSファイルやJavaScriptファイルを複数に分割したくなる時もあるでしょう。しかし、<head>内で外部ファイルの読み込み数が増えるほど、結果的にページの表示は遅れてます。

    ファイルサイズが同じなら変わらないのでは?と思うかもしれませんが、読み込むファイル1つにつき、HTTPリクエストが1回増えてしまうため、ファイル数が多いほど、表示までの時間が遅くなってしまうのです。

    例えば<link>タグで外部CSSファイルを読み込む記述は、サーバーに対して「このCSSファイルをください」という要求(HTTPリクエスト)を投げることです。サーバーはこのHTTPリクエストに対して、該当ファイルを返す(HTTPレスポンス)という処理を行います。

    現在広く普及している通信プロトコルであるHTTP/1.1では、この「HTTPリクエストに対してHTTPレスポンスを返す」という処理を、1つずつしか捌けず、HTTPリクエストの回数が増えるほど、通信に時間がかかってしまいます。

    そのため、ファイルを複数に分割して、複数回のHTTPリクエストを送るよりは、ファイルを結合し、少ない回数でHTTPリクエスト/HTTPレスポンスが行われるようにした方が、結果的には表示速度が改善するわけです。

    6. HTTP/2通信の活用

    先ほどの説明では、HTTP/1.1を前提として、「同じサーバーに対するリクエストは順番に1つずつしか捌けない」と説明しましたが、これを1つずつではなく同時並行で捌くことができれば速くなるのでは?と考えた方もいると思いますが、それが可能なのがHTTP/2です。

    HTTP/2ではマルチタスクでデータ通信が行えるため、HTTP/1.1の時のようにCSSやJSを結合してサイズの大きなファイルにまとめるのとは逆に、むしろ一つ一つのファイルサイズを小さくして細分化した方が通信の高速化に威力を発揮します。

    HTTP/2を利用するには、ブラウザとサーバが対応している必要があります。ブラウザについてはモダンブラウザであればほとんど問題ありません。一方、サーバについては、現状必ずしも対応しているわけではありません。またそもそも通信量が少ないwebサイトでは、HTTP/1.1からHTTP/2に変えることの効果はあまり実感できないかもしれません。

    ただ、こういった諸条件をクリアできるようであれば、HTTP/2の通信を活用してもよいでしょう。

    7. 外部サーバの利用

    「HTTP/1.1だと、HTTPリクエストを1つずつしか捌けない」と説明しましたが、これは1つのサーバーに対して1つずつしか捌けないということであって、別サーバーに対するリクエストであれば、最大6つまで捌くことができます。

    例えば、Aというサーバーに2つのCSSファイルが置いてある場合、これらは1つずつ順番に読み込むしかありません。一方、1つのCSSファイルを別のBというサーバーに設置すれば、これら2つのCSSを並列してダウンロードすることが可能になります。

    このBというサーバーの役割を担うのがCDN(Content Delivery Network)です。

    CDNは世界中に設置されたサーバーにCSSやJavaScriptをアップロードできるため、HTTPリクエストの並列処理が可能になります。また、ページの訪問者にとって物理的に一番近い距離にあるサーバーから自動で取得するようになっているため、場合によっては、自サーバーに置くよりも通信が速くなり、表示の高速化が狙えることもあります。

    ただし、CDNは基本的に有料のため、コストパフォーマンスを考慮した上で使用を判断する必要があります。

    8. CSSの@importの不使用

    @importを用いてCSSを読み込むと、多くの場合で通信速度が遅くなります。

    これは例えば、Google Fontsなどのウェブフォントを使用するときに起りがちです。外部CSSファイルの中から、@importでGoogle FontsのCSSを読み込むと、ブラウザはCSSの読み込みが完了した後に、Google FontsのCSSを順番に読み込んでいくため、その結果、表示が遅くなるのです。

    このような場合は、CSSファイル内から@importで読み込むのではなく、HTMLの<head>タグ内に<link>タグで読み込むようにすることで解決できます。

    また、Google FontsのCSSはGoogleのサーバーに置いてあり「別サーバーへのリクエスト」になるため、<link>タグで読み込めば、並列してこれら2つのCSSをダウンロード可能となり、読み込みや表示の速度を改善できます。

    9. CSSのセレクタの簡素化

    CSSの.parent > .childのようなセレクタについては、ブラウザが解析時に右から左へマッチングしていくため、これらのセレクタの数が多いほど、CSS適用までの時間が長くなってしまいます。

    マッチング処理の流れとしては、まず.childというクラスを持つ要素を探し、その中から親要素が.parentである要素に絞り込みます。つまり、.parent >.childよりも.childだけの指定をした方が、マッチングの回数が減り、処理が早く終わります。

    このような仕組みを理解すると、セレクタを複数指定するのはなるべく避けるべきです。CSSを書くときは子孫構造にせず、シンプルにクラス名一つで指定しましょう。例えば<ul>の子要素<li>は、ul liのように指定しがちですが、<li>にクラスを付与し、そのクラスに対してスタイルを記述した方が、表示の高速化に繋がるわけです。

    10. 不要なコードの削除

    ソースコードに記述された文字数が多く、ファイルサイズが大きくなるほど、ダウンロードにかかる時間とブラウザが解釈する時間が長くなるため、ページ表示速度は遅くなってしまいます。

    つまり表示高速化のためには、不要な記述はできるだけ削除し、ファイルサイズを軽量化するのが鉄則です。

    このような観点から、開発中に生まれた以下のような記述は、納品・公開時には削除しておくべきです。

    • 残す必要のないコメントアウト
    • 使用していないコード

    後述するMinify化で、機械的にファイルを軽量化することができますが、記述されたコードが不要かどうかまでは判断できません。またMinify化と比べても、ファイルサイズを大幅に削れることが多いため、必ず行いたいチェックの一つです。

    11. ライブラリの改良・自作

    JavaScriptを使った高度な処理が手軽に実行できるライブラリはとても便利ですが、これらには、汎用性を高めるために使わないソースコードや処理も多く盛り込まれています。

    例えば、カルーセルやアコーディオンメニューの実現にライブラリを使用すると、ファイルサイズの大きいソースコードを読み込み、そのサイトでは必要がない例外処理なども実行されて、その分、ページが表示されるまでの時間が遅れます。

    また、ユーザーの目に触れるファーストビューで表示される要素に絞って高速に表示させたい場合も、ライブラリでは都合が悪くなることもあります。先にライブラリを読み込んでからJavaScriptの処理が実行されるため、表示の適応までにタイムラグが発生しがちです。例えば、ボックスの高さをJavaScriptで処理する場合、表示されて少し経ってから正しい高さに変更される、といった奇妙な挙動を見せたりします。

    表示の高速化にこだわるのであれば、処理の高速化、軽量化の両方の観点から、ライブラリから不要なソースコードや処理を削除するカスタマイズが有効です。ライブラリのカスタマイズではなく、ファイルを自作した方が良い場合もあります。

    余談ですが、近年はライブラリは使えるが素のJavaScriptは書けない、というコーダーやエンジニアも増えていますが、JavaScriptが書けないと、こういったカスタマイズや自作ができません。便利なライブラリがあるからといってライブラリ頼りになるのではなく、いざとなったら自作できるスキルを身に付けておくことが必要でしょう。

    12. Minify化

    Minify化とは、CSSやJavaScriptのコメントアウトや空白・改行の削除、変数名の短縮化などを、ツールを使って機械的に行うことを言います。ベイジではタスクランナーでCSSやJavaScriptをMinify化していますが、下記のようなウェブサービスでも手軽にMinifyできます。

    Minify化をすると、ファイルサイズはかなり軽量化できますが、軽量化を最優先しているため、人にとっては読みにくい状態になります。例えば、納品後にクライアント側でソースコードを変更する可能性がある、スキルにバラツキがあるチームでの運用が想定される場合などには、Minify化すべきかどうかは考慮する必要があります。

    13. CSSのショートハンドの使用

    CSSのファイルサイズを軽量化するという観点では、ショートハンドを上手に使いたいところです。例えば、下記のようなスタイルの記述だと、「margin-」の部分が重複し、その分の文字数が増えてしまいます。

    .hoge {
      margin-top: 10px;
      margin-right: 20px;
      margin-left: 30px;
      margin-bottom: 40px;
    }

    一方で、下記のようにショートハンドを使用して「margin-」を1つにまとめて省略することで文字数が減ります。

    .hoge {
      margin: 10px 20px 30px 40px;
    }

    非常に微細な影響かもしれませんが、こういった細かな積み重ねこそが、表示高速化に必要な観点です。特に理由がない限り、少しでも高速化に繋がる記述をするということを、日頃から心がけておきたいところです。

    14. DOMアクセス回数の削減

    JavaScriptの書き方も、ブラウザ処理の負荷軽減、表示の高速化に繋がります。

    例えば、for文を使用して<div id="hoge"></div>という要素に<span class="fuga"></span>を挿入する場合。このとき、forが繰り返されるたびに毎回挿入処理を行ってしまうと、その度にブラウザはレンダリングをやり直すため、処理が重くなります。

    そこで以下のように、for文内では挿入したい要素群を変数に保存するだけにし、挿入処理はfor文が終わった後の一回だけにまとめれば、レンダリングの負荷を軽減できます。

    let html = '';
    for (let i = 0; i < array.length; i++) {
      hoge += '<span class="fuga">' + array[i] + '</span>';
    }
    $('#hoge').append(html);

    他に、上記コード例ではfor文の繰り返し回数の上限を、array.lengthで定義していますが、これでは毎度array配列のlength(数)を取得してしまいます。そうではなく、事前に変数に保存しておけば、わずかではありますが、処理の負荷を軽減できます。

    const max = array.length;
    for (let i = 0; i <max; i++) {
      …
    }

    このように、ブラウザがどのようにJavaScriptを解釈して実行しているかを頭の中で想像しながら、ブラウザの処理が少しでも減るような記述の仕方を心がけたいところです。

    15. 画像の圧縮

    ページの表示速度に一番影響がある、と言っても過言ではないのが、画像のファイルサイズです。画像のファイルサイズはなるべく小さくして通信にかかる負担を減らすのは、表示高速化における最重要ポイントといえます。

    このように非常に大事な画像のファイルサイズ軽量化について、私は主に次の4つを実践しています。

    • 最適なファイル形式を選ぶ
    • 適正な解像度(カンバスサイズ)にする
    • 写真のクオリティを下げる
    • EXIF情報など不要な情報を削る

    最適なファイル形式を選ぶ

    画像圧縮以前に、最適なファイル形式を選択する必要があります。画像の透過が必要な場合はPNGかGIF、写真のように色数が多い画像はJPG、図版のような色数の少ないシンプルなものはPNGかGIF、または画像ではありませんが、SVGという選択肢もあります。これらを基本ルールとして、画像の劣化具合やサイズを見ながら、最適なファイル形式を選んでいきましょう。

    適正な解像度(カンバスサイズ)にする

    最近は大画面&高解像度ディスプレイが増えており、リキッドレイアウトで画像を大きく表示できるシーンも増えていますが、画質重視で解像度を無尽蔵に大きくしていくと、どんどんファイルサイズが大きくなってしまいます。クオリティを担保できる必要最低限の解像度を決めることが重要です。

    写真のクオリティを下げる

    基本的に、圧縮時に写真のクオリティを下げると、ファイルサイズは抑えられます。最終的には目視での確認・調整が不可欠ですが、JPEGであれば、オリジナルの90%程度のクオリティであれば、そこまで劣化が目立たないことも多いです。最適な画質については、Photoshopだと画像書き出し時に確認できますが、それ以外には、下記のようなウェブサービスを活用する手もあります。

    EXIF情報など不要な情報を削る

    軽量化としては微々たるものですが、EXIF情報の削除も有効です。EXIF情報とは、画像に含まれている位置や撮影日などの情報です。これらはwebページの表示には不要なので削除します。Photoshopの「Web用に保存」を使うと自動的に削除できます。なお、軽量化以外に、EXIF情報で個人や撮影者、撮影場所が特定されてしまうこともあるので、セキュリティの観点からいっても、削除をおすすめします。

    16. インライン画像化

    通常、画像ファイルはHTTPリクエストを発生して読み込むので、画像数が多いほどこのHTTPリクエストも多くなり、取得にかかる時間が長くなります。しかし実は画像も、CSSやJavaScriptと同じように、外部ファイルにせず、HTMLに直接インラインで記述することができます。それにより、HTTPリクエストの回数を減らすことができます。

    写真のような複雑な画像ではソースコードが長く、HTMLが重くなるので逆効果ですが、SVGやPNGで作ったアイコンのような単純な図形は、インラインで書くことでこの効果を得られます。なお、画像をインラインで記述するには、「base64」というコードに変換する必要があります。これは、WindowsのコマンドプロンプトやGoogle Chromeの拡張機能の他、下記のようなウェブサービスでも行うことができます。

    • データ変換ツール(BASE64, URLエンコード(URLデコード), HEX(16進ダンプ), MD5, SHA-1変換フォーム)
      https://hogehoge.tk/tool/

    変換されたコードは、以下のような書式で<img>タグに埋め込むことができます。

    <img src="data:image/png; base64, base64のコード">

    17. 画像の読み込み遅延

    ブラウザはwebページのロード時にすべての画像を読み込もうとします。そのため、画像の数が多いほどページの表示即ぞに影響が出ます。これを回避できるのが、lazyloadのような、ファーストビューの外にある画像の読み込みを遅延させるプラグインを使用です。ファーストビューの表示には必要がない(ユーザーには見えない)部分の画像の読み込みを遅らせることで、ユーザーは体感的に表示が速くなったように感じられます。

    なお、lazyloadをすべてのブラウザで対応させるには、以下のJavaScriptのプラグインを導入する必要があります。

    また、最新版のChromeではこの機能がネイティブ実装されているため、<img>タグに下記の属性を付け加えるだけで利用できます。

    <img src="hoge.png" loading="lazy">

    18. ローディング表示

    厳密には表示高速化の手法ではありませんが、どうしても表示に時間がかかることが想定される場合には、「読み込み中」と明示するだけで、ユーザーの心象は変わります。何も表示されず真っ白なままでは、待ち時間が長く感じるだけでなく、正常に機能していないとサイトと受け取られて離脱される可能性も出てきます。

    モバイルなどを使っていると、通信環境が悪く、どうしても表示の遅延が発生することもあります。そういう状況も想定して、待ち時間が短く感じられる心理的な配慮も、エンジニア側でできるといいでしょう。

    なおローディングの表示は、あくまで最終手段であり、真っ先に飛びつくものではないと考えています。サイトに訪問したユーザーに、テキストもナビゲーションも一切表示せず、真っ先にローディングを表示させて何秒も待たせるようなことは避けたいものです。

    さいごに

    サイトの表示高速化において「これをやっておけば圧倒的に速くなる」という、切り札のようなものは存在しません。ここで紹介した方法は、10分の1秒も変わるかどうか、というものも多いです。また、サイトの特性やメンテナンス性などを踏まえると、実現が難しいものもあるでしょう。しかしサイトの表示高速化とは、こういった小さなことの積み重ねであるとも思います。

    余談ですが、私はモータースポーツが好きなのですが、例えばF1は、全長4~7kmのサーキットを1周走り切るのに1秒遅れるだけで勝負にならない世界です。トップチームは毎周100分の1秒を競い合っており、そのためにエアロダイナミクス、パワーユニット、ブレーキ、サスペンションなど、1000分の1秒でも速くなる箇所があれば、総動員で車体を改善していきます。その一方で厳しいレギュレーションも守らなくてはなりません。

    サイトにおける表示高速化の考え方には、こういったF1マシンの高速化と共通するものを感じます。数々の制約がある中で小さなことを積み重ねていくことこそ、表示の高速化に繋がり、それが心地よいサイト体験に繋がっていくものだと私は考えています。

    あなたもベイジで働いてみませんか?

    私たちは「人生100年時代を生き抜く強い人材を育てる」を人材育成のテーマとしているweb制作会社です。

    普遍的なスキルが獲得できる環境を作るためにマーケティングに力を入れ、情報発信を積極的に行い、ワークフロー化を推し進めています。

    現在は、マーケター、デザイナー、ディレクター、エンジニア、ライターといったすべての職種を募集しています。ご興味がある方は、以下の採用サイトをご覧ください。

    こんな記事も読まれています

    簡単CSSアニメーション&デザイン20選(ソースコードと解説付き)
    酒井琢郎のプロフィール画像
    酒井琢郎
    540,810View
    BtoBサイトを成功に導く180のチェックリスト
    枌谷力のプロフィール画像
    枌谷力
    129,613View
    WordPressの管理画面を使いやすくする簡単カスタマイズ18選
    酒井琢郎のプロフィール画像
    酒井琢郎
    67,578View
    上に戻る