今回の記事では、外部JavaScriptファイルを、async / defer属性を使用して非同期で読み込む方法について解説します。
JavaScriptは、何もしなければ基本的には同期的に読み込み・実行がされます。
この同期的なJavaScriptが、WEBサイトのパフォーマンスにおいて障害になる場合があります。

同期的なJavaScriptの問題点

同期的なJavaScriptの読み込みは、ドキュメントのパースだけでなく、
CSSの読み込みもブロックする特性があります。

ドキュメントのパースとは、サーバーからダウンロードしたリソースを構文解析し、
各種ブラウザのレンダリングエンジンの内部表現に変換するプロセスのことです。
HTMLはDOMツリーに変換され、CSSはCSSOMツリーに変換されます。

例えば、下記のような記述がheadタグ内にあった場合に、
linkで指定されたCSSの読み込みは、 JavaScriptの読み込み・実行完了までブロックされてしまいます。
ドキュメントのパースがブロックされる = WEBサイトのパフォーマンスの低下に繋がります。

<script src="https://code.jquery.com/jquery-2.2.4.min.js" defer></script>
<script src="./script.js" defer></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="./style.css">

このようにな問題を解決するために、async / defer属性を使用してJavaScriptを非同期に読み込むことができます。
async / defer属性は、<script src=””>のようにsrcが指定されている場合にのみ実行できます。
※単純な<script></script>内に、JSのコードが書かれているタグでは適用ができませんので、注意が必要です。

defer属性

外部JavaScriptファイルを読み込む際に、defer属性を指定することで、JavaScriptファイルを非同期で読み込むことができます。

defer属性を使用した場合、JavaScriptのダウンロードと、ドキュメントのパースが同時進行で進みます。
defer属性を使用した場合のJavaScript実行タイミングは、DOMツリーの構築が完了したタイミングです。

またdefer属性を用いて、複数のJavaScriptファイルを指定した場合に、その実行順序は、記述した順番に保証されます。
下記のような記述の場合には、まずjQueryのライブラリファイルが読み込まれ、その後にscript.jsが読み込まれます。

<script src="https://code.jquery.com/jquery-2.2.4.min.js" defer></script>
<script src="./script.js" defer></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="./style.css">

動作確認

上記の記述を、実際に検証ツールで確認してみると、
読み込みのタイミングは同時に進んでおり、パースをブロックしていないことがわかります。
ロードの優先順位(Priority)もdeferを付与することで、lowになっています。

script.jsの実行結果

$(function() {
    console.log('jQuery 実行');
})

const sections = document.querySelectorAll('section');
console.log(sections);

defer属性で指定したscript.jsは、DOMの取得まで実行できています。
つまり、DOMlツリーの構築が完了したタイミングで実行されていることを証明できます。
同時に、jQueryのコードも実行できているので、読み込み順も保証されていることが分かります。

async属性

外部JavaScriptファイルを読み込む際に、async属性を指定することで、JavaScriptファイルを非同期で読み込むことができます。

async属性を使用した場合、JavaScriptのダウンロードと、ドキュメントのパースが同時進行で進みます。
ここまではdefer属性と同じ性質で動作します。

defer属性との違い

async属性は、defer属性と比較して以下の違いがあります。

  • 実行タイミングが保証されない
  • 実行順序が保証されない

async属性は、JavaScriptが取得されたタイミングで実行されるのが一般的です。
実行タイミングが保証されないということは、記述しているJavaScriptの中に、DOMの要素を取得するものがある場合、エラーになります。

また、実行順序が保証されないということは、
jQueryなどのライブラリファイルに依存した記述方法の場合、先にjQueryのライブラリファイルを実行しないとエラーになります。

動作確認

defer属性と同じように、script.jsを実行しましたが、jQueryのエラーになりました。
これは、async属性が実行順序を保証しないことを証明できます。

関連記事

【サイト改善】ブラウザのレンダリングの仕組みを体型的に解説

Chromeの「Network」を使用したパフォーマンス測定方法