外部JavaScriptを非同期(async / defer)で読み込みレンダリングを高速化する
Googleのサービス「PageSpeed Insights」の解析結果で「修正が必要:スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/CSS を排除する」という項目があります。
レンダリング中にJavaScriptを読み込むことで、画面表示に遅延が生じているという警告のようです。
(CSSについては別途考えます)
そこで、HTML5から追加された属性「async」と「defer」を使って、スクリプトの読み込みを非同期にし、画面表示の高速化を図ってみたいと思います。
概要
HTMLのソースは上から順に解析されます。
ソース上部に大量のJavaScriptを読み込んだ場合、その読み込みに時間がかかる分だけ画面の表示が遅くなります。
少し前までは、JavaScriptはhead部分ではなく、bodyの閉じタグの直前で読み込むことが奨励されていました。
読み込み順を変更することで、レンダリングへの影響を抑えようということですが、この方法では結局、トータルの読み込み時間としては改善していないんですね。
そこでHTML5から実装された属性「async」と「defer」。これを指定すれば、画面の読み込みとは別で外部のJavaScriptを非同期で読み込むので、レンダリングへの影響がなくなるということです。
但し、スライダーやカルーセルなど、見た目を構築するスクリプトについては、その部分の表示に遅れなどが発生します。
「async」と「defer」の違いは、読み込み順を保持するか否かで、性能にはそれほど差はないといわれています。
asyncを導入
まず、asyncを使った方法です。
以下のようにスクリプトタグに属性「async」を指定するだけです。
<script type='text/javascript' src='/js/jquery.min.js' async></script> <script type='text/javascript' src='/js/bootstrap.min.js' async></script> <script type='text/javascript' src='/js/script.js' async></script>
注意しなければならないのは、「asyncは読み込み順を考慮しない」ということです。
つまり、タイミングによって、jqueryを必要とするbootstrapやオリジナルのscriptが先に読み込まれる可能性があります。このケースだと、逆に読み込まれるとスクリプトエラーになってしまいます。
asyncを利用する場合は、それぞれのjsファイルの読み込み順に依存関係がないことが前提となります。
deferを導入
deferを利用する場合は、以下のように指定します。
<script type='text/javascript' src='/js/jquery.min.js' defer></script> <script type='text/javascript' src='/js/bootstrap.min.js' defer></script> <script type='text/javascript' src='/js/script.js' defer></script>
asyncに対して、deferは読み込み順を維持します。
このケースだとjquery、bootstrap、scriptの順で読み込むのでスクリプトエラーの心配はなく、レンダリングの速度向上が期待できます。
APIスクリプトも非同期でも読み込む
Facebook、InstagramやTwitterなどのAPIをページに導入する際、通常JavaScriptで組み込みますが、各々でasyncの設定が可能です。
例えばTwitter widgetの導入は以下のようなコードを設置します。
!function(d,s,id){ var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https'; if(!d.getElementById(id)){ js=d.createElement(s);js.id=id; js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs); } }(document,"script","twitter-wjs");
これに「js.async=true;」を追記することで、非同期読み込みが設定できます。
!function(d,s,id){ var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https'; if(!d.getElementById(id)){ js=d.createElement(s);js.id=id;js.async=true; js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs); } }(document,"script","twitter-wjs");
これらAPIスクリプトは、先ほどのjqueryなどローカルスクリプトとは依存関係がないのがほとんどなので、asyncを使用しても問題ないでしょう。
実際に対策した結果
deferを設置した後、「PageSpeed Insights」で解析してみて「修正が必要:スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/CSS を排除する」の項目が改善していれば成功です。
実際に私の運営している4サイトで、この対策を実行してみて、対策前後で「PageSpeed Insights」解析結果のスコアを比較してみました。
モバイル | パソコン | |||
---|---|---|---|---|
対策前 | 対策後 | 対策前 | 対策後 | |
サイト1 | 54 / 100 | 57 / 100 | 65 / 100 | 66 / 100 |
サイト2 | 70 / 100 | 69 / 100 | 79 / 100 | 77 / 100 |
サイト3 | 69 / 100 | 65 / 100 | 77 / 100 | 75 / 100 |
サイト4 | 70 / 100 | 66 / 100 | 79 / 100 | 77 / 100 |
。。。サイト1では微量ながら改善したようですが、他のサイトではスコアが下がってしましました。なぜ。。
ただ「スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/CSS を排除する」のJavaScriptの項目は表示されなくなり、体感的にも少しレンダリングが早くなったように感じるのですが。
引き続き同項目のCSSを対応して、合わせて評価してみたいと思います。