JavaScriptの読み込みタイミングについて

JavaScriptファイルを読み込ませるときの順序で「サイトの読み込み速度に影響が出るので</body>の直前に入れましょう」というのはずっと昔からのテクニックとして存在しています。現にそうしているサイトはたくさんありますが、「async/defer属性を付与して<head>内で読み込ませる」とHTMLパース(解析処理)を妨げず、結果的にサイト読み込みの高速化に繋げることができます。本記事はJavaScriptファイルの読み込みタイミングについて書いています。

async/defer属性を知るために

async/defer属性に関わる「DOMContentLoaded」、「load」という2つのイベントがあります。イベントとはページの読み込みやボタンのクリックなど、ページで発生させるアクションのことです。

DOMContentLoaded

HTMLパースが完了した時点で発生するイベントです。

load

HTMLパースが完了し、CSSや画像などの読み込みが完了し、JavaScriptのダウンロード・実行など全ての処理が完了した時点で発生するイベントです。

ページの読み込みタイミング

実際どのように読み込まれているかイメージ図で表しました。

読み込みの違い

どちらの属性もつけない(デフォルトの)場合

コード

<script src="js/index.js"></script>

async/defer属性をつけない状態だと、HTMLパース中に<script>タグにたどり着いたらHTMLパースは一時中断され、JSのダウンロードと実行をします。JSの実行が完了するとHTMLパースが再開されます。

async属性をつけた場合

コード

<script async src="js/index.js"></script>

HTMLパースは止まることなくJSのダウンロードが進みますが、ダウンロード完了直後にJSが実行され、実行中はHTMLパースが一時中断されます。<script>タグを複数記述した時の実行順序は<script>タグを記述した順番にはなりませんDOMContentLoadedイベント発生後になる場合はありますが、loadイベント発生よりは前に実行されます。

defer属性をつけた場合

コード

<script defer src="js/index.js"></script>

HTMLパースは止まることなくJSのダウンロードが進みます。JSのダウンロードが完了してHTMLパースも完了した後、DOMContentLoadedイベント発生の直前にJSが実行されます。言い換えるとdefer属性をつけたJSが全て実行されるまでDOMContentLoadedイベントは発生しません。

<script>タグを複数記述した時の実行順序は<script>タグを書いた順番になります

まとめ

読み込み速度は若干ですが「deferasync>属性なし(デフォルト)」の順で速くなります。属性を付けないより付けた方が速いですが、asyncdeferにそれほど大きな差はありません。けれども、async属性は「loadイベント発生よりは前ではあるがいつ実行されるか不明確」です。一方、defer属性は「確実にHTMLパースが完了した後に実行」になるのでHTMLパースを妨げることがないため読み込み速度の向上に繋がります。また、<script>タグを複数記述していて他のJavaScriptファイルに依存し合う場合も、実行順序が決まっているdefer属性が好ましいです。

サイト読み込みの高速化は様々な工夫のチリツモだと思うので、ある程度の高速化であっても、</body>の直前に記述するのではなく、async/defer属性を付与して(個人的にはdefer属性がおすすめ)<head>内で読み込ませるのが良いです。