フロントエンドのパフォーマンスチューニング Javascriptは非同期で読み込むべし
どうもお疲れ様です。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