pagespeedinsightsで『レンダリングを妨げるリソースの除外』する #1
カトウ
JavaScript(JS)を遅延ロードする
PageSpeed Insightsで指摘される『レンダリングを妨げるリソースの除外』を解消する方法を紹介します。
PageSpeed Insightsで指摘される『レンダリングを妨げるリソースの除外』を解消する方法を紹介します。
『レンダリング』とは『≒画像なども含めてページを表示する』ことをいいます。
ブラウザはレンダリングのために、HTMLを解析しDOMツリー(※1)を構築していきますが、その途中に<script>が見つかると、解析を中断し<script>(※2)を実行します。
なので、何も対策がされていない<script>の数だけHTMLの解析が中断され、レンダリングまでの時間がどんどん遅くなってしまいます。
※1 このとき構築したDOMツリーは、開発者ツールのElementsタブから確認することができます。
※2 src属性が付いている場合は、ファイルを読み込みます。
今回は、『レンダリングを妨げるリソースの除外』の中で<script>が原因になっている部分を解消していきます。
デフォルト(同期読み込み+都度実行)
<script type="text/javascript" src="/js/example.js"></script>
この状態では、先ほどの説明どおり、HTMLの解析を中断し/js/example.jsを読み込み、中に書かれているJavaScriptが実行されます。
async属性(非同期読み込み+都度実行)
<script type="text/javascript" src="/js/example.js" async></script>
async属性を付けることで、非同期読み込みに変わり、HTMLの解析を中断しなくなります。
ただし、読み込みが完了すると実行してしまうので、そのタイミングでHTMLの解析が完了していないと、解析が中断されてしまいます。
また、複数のファイルを読み込んでいる場合、それぞれのタイミングで実行するため、<script>を記述(出現)した順番と実行される順番はバラバラになります。
なので、ライブラリなどの実行の順番が重要になるものは、下記のdefer属性を使います。
/js/example-1.js
console.log('example-1.jsを実行しました');
/js/example-2.js
console.log('example-2.jsを実行しました');
index.html
<script type="text/javascript" src="/js/example-1.js" async></script>
<script type="text/javascript" src="/js/example-2.js" async></script>
console
example-1.jsを実行しました
example-2.jsを実行しました
or
example-2.jsを実行しました
example-1.jsを実行しました
defer属性(非同期読み込み+遅延実行)
<script type="text/javascript" src="/js/example.js" defer></script>
defer属性を付けることで、読み込みが完了しても、HTMLの解析が完了するまで実行しないので、解析が中断されることは無くなります。
実行の順番は<script>を記述(出現)した順番と同じになります。
/js/example-1.js
console.log('example-1.jsを実行しました');
/js/example-2.js
console.log('example-2.jsを実行しました');
index.html
<script type="text/javascript" src="/js/example-1.js" defer></script>
<script type="text/javascript" src="/js/example-2.js" defer></script>
console
example-1.jsを実行しました
example-2.jsを実行しました
それぞれのパターンで、読み込みと実行を図にすると以下のようになります。

非同期読み込みをすることで短縮されているのがイメージできたと思います。
その他(遅延読み込み+都度実行)
非同期読み込みとは違いますが、ユーザがスクロールやキー操作など、アクションを起こすまで、読み込まない方法です。
<script>
(function(window,document){
// <script>を追加する関数です
// 第二引数に読み込み完了後のコールバックを設定できます
function insertScript(src,callback=function(){}){
var addScript=document.createElement('script');
addScript.type='text/javascript';
addScript.async=true;
addScript.src=src;
addScript.onload=callback;
var sc=document.getElementsByTagName('script')[0];
sc.parentNode.insertBefore(addScript,sc);
}
// 読み込みたいscriptを指定します
function main(){
// async属性の時と同じ実行の順番はバラバラになります
insertScript('/js/example-1.js');
insertScript('/js/example-2.js');
// コールバックに次の insertScript() を入れることで、defer属性のように実行の順番を指定できます
insertScript('/js/example-1.js',function(){
insertScript('/js/example-2.js');
});
// jQueryプラグインなどの実行もコールバックに入ります
insertScript('/js/jquery.js',function(){
insertScript('/js/jquery.myplugin.js',function(){
$('.myplugin').myplugin([options]);
});
});
}
// 連続してイベントが発生しても1回しか処理しないようにします
var lazyLoad=false;
function onLazyLoad(){
if(lazyLoad===false){
lazyLoad=true;
window.removeEventListener('scroll',onLazyLoad);
window.removeEventListener('mousemove',onLazyLoad);
window.removeEventListener('mousedown',onLazyLoad);
window.removeEventListener('touchstart',onLazyLoad);
window.removeEventListener('keydown',onLazyLoad);
main();
}
}
// スクロールイベントやマウス操作など、ユーザの操作を監視します
window.addEventListener('scroll',onLazyLoad);
window.addEventListener('mousemove',onLazyLoad);
window.addEventListener('mousedown',onLazyLoad);
window.addEventListener('touchstart',onLazyLoad);
window.addEventListener('keydown',onLazyLoad);
window.addEventListener('load',function(){
// 再読み込みなど、既にページがスクロールしている場合
if(window.pageYOffset){
onLazyLoad();
}
});
// 外部のJSから insertScript() を読み込みたいとき
window.lazyScriptLib=window.lazyScriptLib || {};
window.lazyScriptLib.insertScript=insertScript;
})(window,document);
</script>