JavaScriptのスクリプトの非同期読み込み(async, defer)の基礎知識
JavaScriptのスクリプトの非同期読み込み(async, defer)の基礎知識をまとめました。
JavaScriptの非同期読み込み(async, defer)の基礎知識
通常の書き方
通常の書き方は、bodyタグの閉じタグ直前にscriptタグを書くことで、HTMLでJavaScriptを読み込ませます。
その場合、HTMLがパースされた後、scriptのフェッチが始まり、それが終わるとscriptが実行されます。
もし、headに入れてしまうと、HTMLのその上に書かれた部分のみがパースされ、scriptのフェッチと実行が終わるまでメインのHTMLはパースされずに止まってしまいます。
asyncとdefer
理想としては、HTMLもJavaScriptも両方ともパースをして実行できることです。
その一部であるHTMLをパースすることとscriptをフェッチすることを同時並行して行えるのが、スクリプトの非同期読み込み(async, defer)です。
asyncとdeferのどちらもheadタグに入れることで、HTMLをパースすることとscriptをフェッチすることを同時並行して処理できます。
asyncとdeferの違い
asyncとdeferの違いは、scriptの実行のタイミングと順番にあります。
asyncの場合、scriptをフェッチした後即座にscriptが実行されます。
deferの場合、HTMLのパースが完全に終了した後に、scriptが実行されます。
なお、asyncとdeferはheadタグに入れるからこそ、HTMLのパースと同時並行処理できるメリットがでてきます。そのため、bodyタグにいれることはありません。
asyncとdeferのDOMContentLoadedの挙動の違い
asyncの場合、HTMLをパースとscriptのフェッチを同時並行して処理したあと、scriptが実行される間はHTMLのパースが止まります。
DOMContentLoadedを使用すると、asyncのscript以外の実行をまさせることができます。言い換えると、DOMContentLoadedはasyncのscriptは待ちません。
DOMContentLoadedはHTMLのパースが終了した段階で即座に実行されるため、asyncの場合はscriptのフェッチが終わっていない状態でもDOMContentLoadedが発生します。
deferの場合、HTMLのパースとscriptのフェッチの両方が終了した時点で、DOMContentLoadedが発生します。
DOMContentLoadedについては、次の記事を参考にしてみてください。
asyncとdeferの実行の順番の違い
asyncは実行の順番はどうなるかはわかりません。deferは順番通りに実行されます。
そのため、大抵の場合は、deferをheadタグにいれることがベストです。
たとえば外部のライブラリを使うときなどは、asyncだと自分のコードよりも先に実行されることがあるため、deferを使うのが適切です。
順番の無関係な、たとえばGoogle Analyticsなどであれば、asyncでいいでしょう。
そして、古いブラウザはasyncとdeferに対応していない場合があります。
その時は、bodyタグの閉じタグ直前にscriptタグを置きましょう。
asyncとdeferをHTMLへの挿入方法
asyncとdeferをHTMLへの挿入方法はとても簡単です。
通常のscriptにdeferかasyncを記述して、headタグに移すだけです。
<head>
<script defer src="script.js"></script>
<script async src="script.js"></script>
</head>
DOMイベントのライフサイクル
ライフサイクルとは、ユーザーがページアクセスされてからそのページを離れるまでのことです。
DOM content loaded HTMLが完全にパースされたらスタイルシート、画像や外部ソースを待たずに発生させることができます。HTMLをダウンロードしてDOMツリーにいれます。
つまり、画像や外部ソースは後回しにして、HTMLとJavaScriptを先にロードします。
ただし、全体をこれで囲ったりする必要はありません。scriptタグをbodyタグの閉じタグの直前に書いていればOKです。
document.addEventListener('DOMContentLoaded', function (e) {
console.log('HTML paesed and FOM');
})
モジュールの場合
モジュールの場合、type="module"とした時点で自動的にdeferが付与されるため、書く必要はありません。
モジュールに関しては、次の記事を参考にしてみてください。