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を使う
