script要素のJavaScript読み込みパターンについて
どうもoreoです。
今回は、script要素のJavaScript読み込みパターンについて整理したいと思います。defer属性やasync属性を使用することで、JavaScriptの読み込みや実行を制御できるので、パフォーマンスを考える上では非常に重要ですね。
1 JavaScript読み込みパターン
JavaScriptの読み込みパターンは、WHATWGで以下のように分かりやすく纏められています。この図を基に解説していきます!

参考 : https://html.spec.whatwg.org/multipage/scripting.html
1-1 何も指定しない場合
この場合、HTMLパーサーがHTMLを先頭から順に解析し、script要素に遭遇すると、解析を中断してJavaScriptの読み込みと実行を行います。JavaScriptの読み込みと実行の間は、解析処理が中断されるため、JavaScriptファイルが大きい場合、webページの表示に時間がかかります。
<script src="./test.js">

1-2 defer属性を指定する場合
defer属性を指定すると、HTML解析と並行してJavaScriptを読み込み、それらが終了した段階でJavaScriptが実行されます。defer属性が付与されたscript要素が複数存在する場合は、script要素の記載順に実行されます。
defer属性を使用する場合、src属性がないと構文エラーとなります。
<script src="./test.js" defer>

1-3 async属性を指定する場合
aync属性を指定すると、HTML解析と並行してJavaScriptを読み込み、JavaScriptの読み込みが終了した段階でHTML解析を中断して、JavaScriptの実行を行い、実行後にHTML解析が再開されます。async属性が付与されたscript要素が複数存在する場合は、defer属性とは異なり、実行順はscript要素の記載順にはなりません。
aync属もdefer属性と同様に、src属性がないと構文エラーとなります。また、defer属性とaync属性を同時に指定すると、aync属性の挙動となります。
<script src="./test.js" async>

1-4 type属性をmodule に指定する場合
type属性の値としてmoduleを指定すると、リソースはモジュールとして読み込まれるようになります(モジュールスクリプトと呼びます)。この場合、defer属性と同じように、HTML解析と並行してJavaScriptを読み込み、それらの読み込みが終了した段階でJavaScriptが実行されます。
<script type="module" src="./test.js">
また、モジュールスクリプトにasync属性を指定した場合は、「1-3 async属性を指定する場合」と同じ挙動となります。
<script type="module" src="./test.js" async>

2 最後に
どの読み込み方を採用するかはケースバイケースだと思いますので、その都度判断できるよに、各々の違いをしっかり理解しておきたいですね。
3 参考
Discussion
非常にわかりやすい記事ありがとうございます!
nitpickですが、
**defer属性と同じ**ようにのところ、太字にしようとしてtypoしてるみたいですikutaさん!コメント&ご指摘ありがとうございました。修正させていただきました!!