script要素の位置とasync/defer
script
要素はbody
の末尾に記述するのが定説ですが、あるサイトについてscript
要素の記述位置やasync
/defer
が及ぼすパフォーマンスへの影響を実験しました。
- 外部JavaScript 11ファイル・250kb
- 因子1
head
要素内・body
要素の末尾を比較 - 因子2
defer
・async
・それらの属性なしを比較 - いずれのケースもスクリプトエラーがなく期待通りに動作
- PageSpeed Insightsのモバイルスコアで評価
※ この記事ではscript
要素はsrc
属性で外部参照することを前提にします。インラインスクリプトではありません。
※ また、1サイトでの結果に過ぎないのでその点も考慮ください。
最速はasync付きでhead要素に記述
PageSpeed Insightsのスコアはばらつきが出るので、10回の計測を行い、上下2個は外れ値として除外中央6個の平均(ExcelのTRIMMEAN
関数)を計算した結果がこちらです。
属性なし | async | defer | |
---|---|---|---|
script要素をhead内に記述 | 19 😢 | 63 😁 | 36 |
script要素をbody末尾に記述 | 45 | 29 | 29 |
async
要素はほぼすべてのブラウザが対応済み。今後は**「head
要素にasync
付きで記述する」**を基準にしていきたいと思います。
<!-- 従来の外部参照script要素の書き方 -->
<body>
...省略...
<script src="/path/to/script.js"></script>
</body>
<!-- これからの外部参照script要素の書き方 -->
<head>
...省略...
<script src="/path/to/script.js" async></script>
...省略...
</head>
安易なasync/deferは逆効果になることも
属性なしでページの上部にscript
を記述するのは、出だしからレンダリングをがっちりブロックするのでいちばんの悪手です。これは昔からの常識です。
body
末尾のscript
要素にdefer
やasync
を追加するとかえってパフォーマンスが悪化しました。これは意外な結果でした。
body
末尾に記述するということ自体が、レンダリングブロックを避ける擬似的なdefer
なので、むやみに遅らせてもLong taskの終点をただ先送りするだけなのかなと推測しています。
今回のケースではdefer
自体、あまりいいところがない結果になりました。
asyncの利用は計画的に
async
は強力ですが、むやみに付けるとスクリプトが期待通りに動作しないことがあります。また、読み込み序盤で必要なスクリプトをasync
にするのも逆効果です。
以下の点を注意しながら使っています。うろおぼえなので別の機会にプラクティスをまとめてみたいところです。
- インラインスクリプトには
async
が使えないので注意。load
イベントにくるむなど実行遅延が必要かも。 - DOMContentLoadedイベントを受け取れない。その後にスクリプトが実行されるので当然ながら。
- jQuery.readyは問題なし。document.readyStateを見て、読み込み後であれば即時実行するから。
- JavaScriptに依存するレイアウトは少なくともファーストビューでは避ける。カルーセルスライダーなどはJavaScriptオフでも表示崩れがないものを使う。
-
async
を付けた要素間の実行順序は入れ替わる可能性があるので依存関係には要注意。ただしChromeでは今のところ実行順序の入れ替わりが確認できないので詳細は要調査。