HTML のパフォーマンスを考える時、 <script>
タグを用いたスクリプトの読み込みを最適化することが多々あります。<script>
タグに指定する属性には主に async
と defer
がありますが、調べた限り日本語の多くのサイトでは詳しい比較がなされていなかったので書きました。
script タグの読み込みに関わる属性
画像を引用します:
- https://stackoverflow.com/questions/13821151/can-you-use-both-the-async-and-defer-attributes-on-a-html-tag/68929270#68929270
- https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
無属性または blocking
を指定
<script>
タグが出現した時点で HTML のパースを中断し、スクリプトをダウンロード・実行します。DOM の読み込みが中断されるため、避けるべきです。
defer
を指定
<script>
タグが出現すると HTML のパースとスクリプトのダウンロードを並行して行います。HTML のパースが終了するとスクリプトを実行します。複数のスクリプトがある場合 HTML に記述された順に実行されます。
async
を指定
<script>
タグが出現すると HTML のパースとスクリプトのダウンロードを並行して行います。スクリプトのダウンロードが終わった時点で HTML のパースを中断してスクリプトを実行します。複数のスクリプトがある場合でも実行順は保証されません。
type="module"
を指定
スクリプトをモジュールとして読み込みます。デフォルトで defer
と同じ挙動、async
を指定すると async
と同じ挙動になります。
async
と defer
のどちらを使うか
上の図を見るとスクリプトの実行終了までにかかる時間は同じように思えます。日本語の多くのサイトではこれをもとにどちらでも変わらない、もしくは defer
のほうが DOMContentLoaded
を待つため無難とされていますが、複数ファイルの場合この解釈は間違いです。(DOMContentLoaded
を待つ必要があるならばイベントリスナを設定すべきです)。
例えば HTML が次のようにスクリプトを読み込んでいるとします。
<script src="script1.js" async></script>
<script src="script2.js" async></script>
<!-- または -->
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
defer
の場合 script1.js
のダウンロード終了が script2.js
のダウンロードと HTML のパースより遅いと script1.js
のダウンロード終了と実行を待つ空白の時間が発生します。一方 async
を指定した場合は script2.js
が script1.js
を待たず先に実行されます。すなわち defer
の場合は script2.js
の実行時間分だけ読み込みにかかる時間が長くなります。
したがってできるだけ async
を使ったほうがスクリプトの実行が早く終る可能性があります。ただし script1.js
がグローバルに定義したオブジェクトを script2.js
で使う場合など、ファイルの依存関係がある場合は defer
を用いる必要があります。
判断基準
できるだけ async
、難しいなら defer
、最悪の場合無指定 (blocking
) にすべきだと考えられます。
-
document.write
を使用する場合、無指定 (blocking
) にする- そもそも
document.write
を使うべきではない
- そもそも
- スクリプトごとに依存関係がある場合
defer
を用いる- CDN のライブラリがグローバルにオブジェクトを作る場合など
- できればモジュール化してスクリプトから
import
を使うなど他のファイルへの依存をなくしたい
-
async
を使う