トレンドはフレンド

defer、asyncでscriptを非同期で読み込み、表示速度を高速化する

この記事は約4分で読めます。

よこのじ(@yokonoji_work)です。

Webサイトの表示速度を低下させるレンダリングブロックとは何か?で、scriptタグにdeferやasync属性を付けると非同期で読み込みが可能になると紹介しました。ここでは、deferとasyncの挙動の違いを確認したいと思います。

属性による挙動の違い

scriptタグに属性を付けない場合、deferを付けた場合、asyncを付けた場合の挙動を確認します。

下記、前提知識となります。

  • jsファイルの同期的なダウンロード中はhtmlのパース(解析)が中断される
  • スクリプト(プログラム処理)実行は属性の有無によらず同期処理なのでパースが中断される

属性なしの場合

パースが中断されるのは、ダウンロード時間+スクリプト実行の時間です。

htmlのパースは記述コードの上から実行されます。scriptタグに属性がない場合は、scriptタグに到達したタイミングでダウンロードを行い、ダウンロード中はパースが中断されます。また、ダウンロードが完了するとすぐにスクリプトを実行しますので、ダウンロード+スクリプト実行の時間分パースが中断されます。

defer属性の場合

非同期にてパースと並行してダウンロードされるため、パースは中断されません。スクリプトはDOMが構築されてから、DOMContentLoadedイベントのタイミングで実行されます。

DOMContentLoadedは、HTMLドキュメントの読み込みと解析が完了した時に発火し、スタイルシートや画像、サブフレームの読み込みが終わるのを待ちません。

loadは、リソースおよびその依存リソースの読み込みが終わると発生します(スタイルシートや画像などの読み込み完了を待つ)。

Window: DOMContentLoaded イベント - Web API | MDN
DOMContentLoaded イベントは、最初の HTML 文書の読み込みと解析が完了したとき、スタイルシート、画像、サブフレームの読み込みが完了するのを待たずに発生します。

複数のscriptタグにdeferを付けた場合は上から順番にスクリプトが実行されます。

<script src=”1.js” defer></script>
<script src=”2.js” defer></script>

この場合は、1.js > 2.jsの順番にスクリプトが実行されます。

<script src=”1.js” defer></script>
<script src=”2.js”></script>

2.jsの方にdeferが付いていない場合、2.jsのスクリプトはパースを中断して実行され、その後DOMが構築されてから1.jsが実行されます(2.js > 1.js)。

<script defer>を複数記述した場合、実行順序は保証されるでしょうか?

async属性の場合

asyncも非同期にてパースと並行してダウンロードされるため、ダウンロード中にパースは中断されません。しかし、ダウンロード完了後すぐにスクリプトが実行されますので、パースが完了する前にスクリプトが実行されてパースを中断することがあります。パースが完了した後でのスクリプト実行であれば影響はありません。

スクリプトはloadイベントの発生までには実行されますが、DOMContentLoadedイベントの後に実行される可能性はあります。そのため、DOMContentLoadedが処理の条件になっている場合、DOMContentLoadedが検知できずに処理が実行されないという罠にハマるかもしれませんので注意が必要です。

Async-loaded scripts with DOMContentLoaded or load event handlers not being called?

また、複数のscriptタグにasyncを付けた場合、deferと異なり実行の順番は保証されません。実行順が重要なスクリプトの扱いには注意が必要です。

 

[toggle title=”参考サイト”]

[/toggle]

タイトルとURLをコピーしました