📖

フロントエンドのパフォーマンスチューニング Javascriptは非同期で読み込むべし

2024/01/26に公開

どうもお疲れ様です。MESIです。
フロントエンドのパフォーマンスチューニング手法の一つとしてJavaScriptを非同期で読み込むことを学んだので忘備録として残します。

なぜ非同期で読み込む必要があるのか?

主な理由としてレンダリング時のブロックを回避するためにJavaScriptを非同期で読み込む必要があります。

レンダリングブロックについて

通常、ブラウザはHTMLを上から下へと解析し、表示していきます。
しかし、JavaScriptファイルが同期的に読み込まれる(<script>タグのデフォルトの挙動)と、ブラウザはそのファイルのダウンロードと実行が完了するまでHTMLの解析を停止します。
これによりページのレンダリングが遅れ、ユーザーがページの内容を見るまでの時間が長くなります。

そこで非同期読み込みの出番です。
非同期でJavaScriptを読み込む(asyncまたはdefer属性、またはJavaScriptによる動的な読み込み)場合、HTMLの解析がJavaScriptのダウンロードや実行によってブロックされません。これにより、ユーザーがページの主要なコンテンツをより早く見ることができます。

どうやって非同期で読み込みするか?

defer属性をつける

scriptタグで外部のJSを読み込む際にdefer属性をつけることで、読み込みが非同期で行われる。
実行タイミングはドキュメントがパースされた後。
次に紹介するasync属性とは違い、複数のscriptタグを読み込んでも実行の順番は保証される。

<script defer src="script-0.js"></script>
<script defer src="script-1.js"></script>
<script defer src="script-2.js"></script>

この場合はscript-0、script-1、script-2の順番で読み込まれる。

async属性をつける

async属性でもJavaScriptを非同期で読み込むことができる。
しかし実行タイミングはHTMLの解析が完了する前でもスクリプトが取得され次第実行なのでdeferとは違い読み込みの順序は保証されない。

<script async src="script-0.js"></script>
<script async src="script-1.js"></script>
<script async src="script-2.js"></script>

このように読み込んでも、どのファイルから実行されるかはそのときによって変わる。

defer属性、async属性を使わずにJavaScriptを非同期で読み込む

JavaScriptから動的にscriptタグを作成し、そのscriptタグにソースを設定してDOMツリーに追加を行うことで非同期読み込みが可能。

var script = document.createElement('script');
script.src = 'path/to/your/script.js';
document.head.appendChild(script);

古いブラウザではdeferやasyncがサポートされていないことがあるので、そういう場合はこちらを使うと良いでしょう。

まとめ

  • defer属性
    • ドキュメントのパースをブロックしない
    • 実行タイミングはドキュメントのパース後
    • 実装順は宣言順で実行
  • async属性
    • ドキュメントのパースをブロックしない
    • 実行タイミングはスクリプトがダウンロードされた直後で、HTMLの解析が完了する前であっても実行
    • 実行順は宣言順で保証されない
  • 通常のscript要素
    • ドキュメントのパースをブロックする
    • 実行タイミングは同期的
    • 実行順は宣言順で保証

Discussion