Help us understand the problem. What is going on with this article?

PageSpeed Insightsで100点を取るためにやったこと

More than 3 years have passed since last update.

はじめに

内部SEOとして、PageSpeed Insightsで高得点をとる……なんて話を昔から、チラホラ聞きます。
ページ速度がSEOにどれほどの影響があるのかはわかりませんが、ページ表示速度の改善を行って悪いことはないはずなので、私が運営している個人サイトをPageSpeed Insightsで100点とれるように頑張ってみました。

本記事では、レンダリングブロック周りを主に取り扱っていきたいと思います。

目標

  • デザインは崩さない
    • よく見る対応として、「外部CSSをbodyの直後で読み込むようにして……」というのがあるけど、そうすると、一度CSSが当たらないページが出てしまう。美しくないので、ファーストビューではCSSが当たった状態で表示する
  • maxCDN等を利用している部分は、そのまま使う
    • 個人サイトなので、CDNを使用しているのはサードパーティー製のJSやCSSのみだが、これはこのまま使う
  • 無理をしてJS、CSSをまとめない
    • 外部CSSやJSを無理してまとめると、各ページ毎に作られることになり、ブラウザキャッシュが有効利用されにくい
    • HTTP2にすれば、同時接続の制限もなくなるし……

静的コンテンツをキャッシュさせる

画像、JS、CSSは最低2週間、できれば1年間キャッシュさせて下さい。

今回は、3週間にします。

PHPで出力している場合は、

<?php
        header('Content-type: text/css');
        header('Cache-Control: max-age='.(60 * 60 * 24 * 21).', public');
        $path = 'xxxxxxx';
        header('Last-Modified: '. date('F d Y H:i:s', filemtime($path)).' JST');
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($path)) {
            header('HTTP/1.1 304 Not Modified');
            die;
        }
        echo file_get_contents($path);

Apacheの場合は、

    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
    ExpiresActive On
    ExpiresByType text/css "access plus 3 week"
    ExpiresByType text/javascript "access plus 3 week"
    ExpiresByType application/javascript "access plus 3 week"
    ExpiresByType application/x-javascript "access plus 3 week"
    ExpiresByType image/jpeg "access plus 3 week"
    ExpiresByType image/png "access plus 3 week"
    ExpiresByType image/gif "access plus 3 week"
    #フォント
   ExpiresByType application/vnd.ms-fontobject "access plus 3 week"
   ExpiresByType application/x-font-ttf "access plus 3 week"
   ExpiresByType application/x-font-opentype "access plus 3 week"
   ExpiresByType application/x-font-woff "access plus 3 week"
   ExpiresByType image/svg+xml "access plus 3 week"
    FileETag MTime Size

こんな感じ。

nginx の場合は、

location ~ ^/static_contents/ {
  add_header Cache_Control "max-age=1814400, public";
}

こんな感じ。

レンダリングブロックしているJSをなんとかする

<script src="..."></script>はレンダリングブロックするので、PageSpeed Insightsに怒られます。

これをまずは、なんとかします。

とりあえず色々省略した元HTML

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>

    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script>
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    </script>


    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js"></script>
    <script>
    $('.boot-switch').bootstrapSwitch();
    </script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js"></script>

  </body>
</html>

PageSpeed Insightsの解析結果

7a5a22fc1ce384ebcb386a264587aa11.png

解決方法としては、2つあり、asyncを付けるか、deferを付けるかのどっちかになります。

  • async
    • 非同期でJSを読み込み、可能な限りはやく実行
  • defer
    • 非同期でJSを読み込み、ページを最後まで読み込んだら順番に実行

asyncは、読み込まれると同時に実行されますので、実行順序は保証されません。
単体で解決しているならともかく、依存がある場合は使用できません。

deferはページが読み込み終わるまで実行されませんが、実行順序は保証されます。

JQuery使ってるし、Bootstrapも使ってるので、defer一択です。

読み込んでいる、Script全部に、deferを付けたら、実行順序も保証されるし、そのまま動くよね!

7fcf3bd5503b7089bfbd2d42e347ec5f.png

orz...動きませんでした。

deferすると、インラインで記述された依存コードが動かなくなります。

全部外に出すのはめんどくさいので、色々試してみたところ、

    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>

こんな感じで、scriptタグのonloadイベントの中に依存コードを入れれば、解決するっぽいです。

※あと、外部JSの中でdocument.writeしている場合もNGのようです。

変更後HTML

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>

    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" defer></script>

    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" onload="$('.boot-switch').bootstrapSwitch();" defer></script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js" defer></script>

  </body>
</html>

点数は変わりませんが、JSのエラーは消えました。

9ec682d1a3fb6bb6c9d69c6e2e21e86f.png

CSSを遅延レンダリングさせる

CSSはHTML単体で、どうにかする方法はないので、AsyncとDeferを行う事ができる、JSを作ってみました。

DLはこちら⇒PrefetchLoader

使い方は簡単。
今回の場合であれば、

<script src="/js/PrefetchLoader.js" async onload="
(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true // deferで読み込む
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true // asyncで読み込む
    }
]


);
prefetch.allAsync();


}());


 "></script>
 <noscript>
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
 </noscript>

このJSは、単独で動作するので、asyncで読み込みます。

やってることは、
preloadもしくはprefetchを利用してCSSを非同期で読み込み、読み込み終わったら、レンダリングさせているだけです。

これでCSSの遅延読み込みは完了!

9b744b189450c1b48fd4ca64c4aad556.png

と思いきや、デザインが崩れてしまいました。

どうやら、CSSを非同期にしたことで、CSSに依存したインラインJSがおかしくなったようです。

具体的には

    <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" onload="$('.boot-switch').bootstrapSwitch();" defer></script>

これ。ここの部分。

これを解決するには、CSSが適応されてから、onloadに指定したJSを実行してやればいいので、

  <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" defer onLoad="
    window.addEventListener('load', function(){
      $('.boot-switch').bootstrapSwitch();
    }, false );
  "></script>

こんな感じで書けば、問題なくなった。

もしくは、PrefetchLoaderpreload_successでやるか。

a67404f6b119dec90329e426967aa6e0.png

……あれ?
むしろ点数下がっているんですが……?

しかも、CSSのレンダリングブロックが解除されていないし、なんかエラーも増えてる。

そして、増えたエラーメッセージはこれ。

c3e0f00b33adfbc75dc034ac05b094e0.png

エラーメッセージを読むと、

非同期読み込んでるCSSってやっぱりレンダリングに必要だったんじゃない?
5%しかレンダリングできなかったよ?

ってことですね。

図らずも、目標として掲げた、「ファーストビューでキチンとstyleを当てる」という事をやれば、解決しそうですね。

とはいえ、使用しているCSSをすべてインラインにするのは現実的ではありません。
ファーストビューで、適応されているCSSだけ抜き取ってインラインCSSにしたい。

そこで、gulp-uncssを使います。

npmがインストールされていれば、

↓のpackage.jsonを配置して、npm installするだけ。

package.json
{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "keywords": [
  ],
  "author": "Suzunone",
  "license": "",
  "homepage": "",
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-uncss": "^1.0.6"
  }
}

つぎに、ページで読み込まれているCSSすべて一纏めにして、package.jsonを配置したディレクトリに、site.cssとして配置します。

そして、以下のgulpfile.jsを、同じディレクトリに配置します。

gulpfile.js
var gulp = require('gulp');
var uncss = require('gulp-uncss');

gulp.task('default', function () {
    return gulp.src('site.css')
        .pipe(uncss({
            html: ['https://xxxxxxxxx/test.html'] //ここは任意のurlにして下さい
        }))
        .pipe(gulp.dest('./out/'));
});

ディレクトリ構造は、こんな感じになります。

├── gulpfile.js
├── node_modules
├── package.json
└── site.css

配置し終わったら、

gulp

とコマンドを実行すると、ファーストビューで使用されるCSSだけが、outディレクトリに書き出されます。

├── gulpfile.js
├── node_modules
├── out
│   └── site.css
├── package.json
└── site.css

書き出されたCSSを

内に、インラインで記述する。
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>


<script src="/js/PrefetchLoader.js" async onload="
(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true
    }
]


);
prefetch.allAsync();


}());


 "></script>
 <noscript>
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" as="style" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <link rel="stylesheet" as="style" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css">
    <link rel="stylesheet" as="style" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
    <link rel="stylesheet" as="style" href="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css">
 </noscript>
<style>
html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}button,input{margin:0;font:inherit;color:inherit}button{overflow:visible}button{text-transform:none}button{-webkit-appearance:button;cursor:pointer}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input{font-family:inherit;font-size:inherit;line-height:inherit}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=checkbox]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-group{margin-bottom:15px}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:focus,.btn-success:active:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active{background-image:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-block{display:block;width:100%}.container-fluid:after,.container-fluid:before,.row:after,.row:before{display:table;content:" "}.container-fluid:after,.row:after{clear:both}@-ms-viewport{width:device-width}
.btn-success{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-success:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active{background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success:active{background-color:#419641;border-color:#3e8f3e}
 body{position:relative}body{padding-top:50px}.url_input{border:1px solid #FF8000}
.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
</style>

  </head>
  <body>

    <div class="container-fluid">
      <div class="row">
        <form class="form-search subscribe-container" method="post" action="" role="form">
            <div class="form-group"> <input type="text" value="" name="xxxxx" class="form-control input-lg url_input" placeholder="入力して下さい。" required> </div>
            <div class="form-group"> <button type="submit" class="btn btn-success btn-lg btn-block">送信</button> </div>
            <div class="form-group"> <label>スイッチ<br><input type="checkbox" value="yes" name="switch_a" class="boot-switch"></label> </div>
        </form>
      </div>
    </div>



    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" onload="
        $(function() {
            $('form').submit(function() {
                $('form').find(':submit').attr('disabled', 'disabled');
            });
        });
    " defer></script>
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" defer></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js" defer onLoad="
    window.addEventListener('load', function(){
      $('.boot-switch').bootstrapSwitch();
    }, false );
  "></script>

    <script src="//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js" defer></script>

  </body>
</html>

370ecc7967efee506783d7d8e9469c87.png
83913086c21effb4b4566715c9bae436.png

やったぜ。

画像を圧縮(最適化)する

幸いながら、動的に画像が増えるような機能はないため、皆大好きtinypng(怪しいパンダのアイツ)で何とかなりました。

動的に最適化する場合は、Google推奨の方法で対応して下さい。

最後に

実際には、サードパーティー製の解析タグや、広告タグがあり、100点を取ることは不可能かも知れませんが、かなり近づけることはできると思います。
そして、キチンと対応すると、確かに表示速度は速くなりました。

サーバーのレスポンスタイムは変わらなくても、体感的に早くなった気がします。
(プラシーボ効果かもしれませんが)

今回作った、PrefetchLoader.jsは、CSSの遅延ロード以外にもコンテンツの先読みに使用できますので、より一層の早さを目指すなら、

(function () {
var prefetch = new PrefetchSequence(
[
    {
        urls: [
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.min.css',
        ],
        default_resource: 'style',
        using_defer_css_load: true
    },
    {
        urls: [
            '//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.css',
        ],
        default_resource: 'style',
        using_async_css_load: true
    },
    {
        urls: [
            '//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js',
            '//netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js',
            '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js',
            '//cdn.jsdelivr.net/sweetalert2/6.6.2/sweetalert2.min.js'
        ],
        default_resource: 'script',
    }
]

]


);
prefetch.allAsync();


}());

こんな感じで、HTMLの最後で読み込まれるJSを先読みさせたり、

画像のlazyLoadを行っている場合に、

window.addEventListener( 'load', function(){
    var prefetch = new PrefetchLoader({
        href_attribute_name:'data-src'
    });
    prefetch.all('img[data-src]');


}, false );

こんな感じで、通信の余剰を使って、先に画像を読み込んだりできます。

是非試してみて下さい。

プルリクもお待ちしております。

更新履歴

  • Apacheの設定にフォントの設定が抜けていました
suzunone
主にWEB系のエンジニア
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした