ソーシャルボタンを自作して表示スピードを高速化
Twitter、Facebook、はてな、Pocketなど公式のボタンを使用するとかなりページを表示するスピードが遅くなるため、先日からソーシャルボタンの自作にチャレンジしています。Wordpressであればプラグインで簡単に高速化できのですが、Wordpressではないページで使用するため、どうしても自作するしか高速化する方法がありません。そこで最近行っている自作チャレンジしているメモのブログが以下なんですが、いろいろな方法を試して徐々にページスピードを上げることができました。
- PHPでPocketの公式ボタンからストックされたカウント数だけを取得する
- PHPでカウント付のSNSシェアボタンを自作してみた
- PHPのcurl_multiを使って非同期でカウント数を取得するSNSのシェアボタンを自作してみた
上から、Pocketのストックカウントを取得するだけで約1.4~2秒かかり、公式ボタンより遅くなる。
↓
PHPの関数「cURL」を使用してTwitter、Facebook、はてな、Pocketのカウント数をAPIから同期で取得してボタンを自作して約2秒で表示できた。
↓
PHPのcURLを非同期で実行できるcurl_multiを使用すると同じことを約1.5秒で表示できるようになりました。
でも同期でPHPを読み込むから1.5秒遅くなるということ
自作のSNSボタンのみの表示を約1.5秒まで縮めることができたのですが、このSNSボタンを表示させたいページに埋め込むと、そのページの表示は1.5秒遅くなるということ。curl_multiでAPIの取得は非同期でやっているけど、そのPHPはページから同期で呼び出すためです。これを非同期で呼び出すことができればページ全体の表示を速くできるのではないかと思います。
そこでPHPをJavascriptから呼び出す!
jQueryのAjaxでPHPを非同期で呼び出すことができるそうです。これを使えば、PHP内で各SNSのAPIからカウント数を非同期で取得し、そのPHPをJavascriptから非同期で実行させることがきてダブル非同期。
サーバーサイドのPHPとクライアントサイドのJavascriptで負荷を分散してるので、これで更に速くなるに違いない!と想像しています。
そして作ったソース
PHP
PHPではjavascriptのページから$_GETでカウントを取得したいURLを受取り、そのURLを対象に各SNSのAPIからカウント数を取得します。そしてカウント数を組み込んだ自作シャーシャルボタンのHTMLを変数に入れます。ファイル名は仮にget_sns_button.phpとします。
<?php function num_format($count) { //3桁区切りのカンマを削除 $count = str_replace(',','',$count); //数値に変換 $count = intval($count); // カウント数が取得できないときは0にする return isset($count) ? intval($count) : 0 ; }
function fetch_sns_count($targeturl) { // 並列通信用マルチハンドルを用意 $mh = curl_multi_init();
// APIごとにCurl Handleを作る // Twitter $ch_twitter = curl_init("http://jsoon.digitiminimi.com/twitter/count.json?url=$targeturl"); curl_setopt($ch_twitter, CURLOPT_RETURNTRANSFER, TRUE); curl_multi_add_handle($mh, $ch_twitter);
// Facebook $ch_facebook = curl_init("https://graph.facebook.com/?id=$targeturl"); curl_setopt($ch_facebook, CURLOPT_RETURNTRANSFER, TRUE); curl_multi_add_handle($mh, $ch_facebook);
// Hatena $ch_hatena = curl_init("https://b.hatena.ne.jp/entry.count?url=$targeturl"); curl_setopt($ch_hatena, CURLOPT_RETURNTRANSFER, TRUE); curl_multi_add_handle($mh, $ch_hatena);
// Pocket $ch_pocket = curl_init("https://widgets.getpocket.com/v1/button?v=1&count=horizontal&url=$targeturl&src=$targeturl"); curl_setopt($ch_pocket, CURLOPT_RETURNTRANSFER, TRUE); curl_multi_add_handle($mh, $ch_pocket);
// 複数の通信を非同期で同時実行 do { curl_multi_exec($mh, $running); } while ( $running );
// APIから戻ってきた値を変数に入れる $json_twitter = curl_multi_getcontent($ch_twitter); $json_facebook = curl_multi_getcontent($ch_facebook); $count_hatena = curl_multi_getcontent($ch_hatena); $html_pocket = curl_multi_getcontent($ch_pocket);
// cURLハンドルセットを閉じる curl_multi_remove_handle($mh, $ch_twitter); curl_close($ch_twitter); curl_multi_remove_handle($mh, $ch_facebook); curl_close($ch_facebook); curl_multi_remove_handle($mh, $ch_hatena); curl_close($ch_hatena); curl_multi_remove_handle($mh, $ch_pocket); curl_close($ch_pocket); curl_multi_close($mh);
/*デバッグ用 var_dump($json_twitter); var_dump($json_facebook); var_dump($count_hatena); var_dump($html_pocket); */
//Twitterのカウント数を取り出す $count_twtter = json_decode($json_twitter, true ); $count_twtter = num_format(strval($count_twtter['count']));
//facebookのカウント数を取り出す $count_facebook = json_decode($json_facebook, true ); $count_facebook = num_format($count_facebook['share']['share_count']);
//Hatenaのカウント数を取り出す $count_hatena = num_format($count_hatena);
//Pocketのカウント数を取り出す //HTML→DOM→XML→JSON→配列に変換 $dom_pocket = new DOMDocument('1.0', 'UTF-8'); $dom_pocket->preserveWhiteSpace = false; $dom_pocket->loadHTML($html_pocket); $xmlString_pocket = $dom_pocket->saveXML(); $xmlObject_pocket = simplexml_load_string($xmlString_pocket); $count_pocket = json_decode(json_encode($xmlObject_pocket), true); $count_pocket = num_format(strval($count_pocket['body']['div']['a']['span']['em']));
//カウント付ソーシャルボタンのHTML $res = '<ul class="sns-counter" title="'.$targeturl.'"><!--'; $res .= '--><li class="btn-tw">'; $res .= '<a href="//twitter.com/intent/tweet?url='.$targeturl.'" target="_blank">'; $res .= '<i class="fab fa-twitter"></i><span>'.$count_twtter.'</span>'; $res .= '</a>'; $res .= '</li><!--'; $res .= '--><li class="btn-fb">'; $res .= '<a href="//www.facebook.com/sharer/sharer.php?u='.$targeturl.'" onclick="window.open(\'https://www.facebook.com/sharer/sharer.php?u='.$targeturl.'\', \'new\', \'width=500,height=300\');return false;" target="_blank">'; $res .= '<i class="fab fa-facebook-f"></i><span>'.$count_facebook.'</span>'; $res .= '</a>'; $res .= '</li><!--'; $res .= '--><li class="btn-hb">'; $res .= '<a href="//b.hatena.ne.jp/entry/'.$targeturl.'" target="_blank">'; $res .= '<i class="fa fa-hatena"></i><span>'.$count_hatena.'</span>'; $res .= '</a>'; $res .= '</li><!--'; $res .= '--><li class="btn-pk">'; $res .= '<a href="//getpocket.com/edit?url='.$targeturl.'" target="_blank">'; $res .= '<i class="fab fa-get-pocket"></i><span>'.$count_pocket.'</span>'; $res .= '</a>'; $res .= '</li><!--'; $res .= '--><li class="btn-line">'; $res .= '<a href="//social-plugins.line.me/lineit/share?url='.$targeturl.'" target="_blank">'; $res .= '<i class="fab fa-line"></i><span>LINE</span>'; $res .= '</a>'; $res .= '</li>'; $res .= '</ul>'; return $res; } $targeturl = $_GET['targeturl']; echo fetch_sns_count($targeturl); ?>
Javascript
jQueryのAjaxからPHP(get_sns_button.php)を呼び出し、PHPの変数に入ったソーシャルボタンのHTMLをページ内に展開するページのソースです。
<!DOCTYPE html> <html lang="ja" itemscope itemtype="http://schema.org/WebPage"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta http-equiv="x-dns-prefetch-control" content="on"> <link rel="dns-prefetch" href="https://ajax.cloudflare.com"> <link rel="dns-prefetch" href="https://cdn.jsdelivr.net"> <title>無題ドキュメント</title> <style> .sns-counter { width: 100%; } .sns-counter li { display: inline-block; border-radius: 2px; width: 80px; padding: 3px; margin-left: 3px; } .sns-counter li span { display: inline-block; text-align: center; width: 70%; font-size: 12px; font-weight: bold; color: #fff; } .sns-counter li i { display: inline-block; text-align: center; width: 30%; color: #fff; font-size: 14px; position: relative; top: 1px; } .sns-counter .btn-tw { background-color: #55ACEE; } .sns-counter .btn-fb { background-color: #3A5795; } .sns-counter .btn-hb { background-color: #008CE1; } .sns-counter .btn-pk { background-color: #ef4357; } .sns-counter .btn-line { background-color: #1dcd00; } .sns-counter .btn-total { background-color: #000; } .btn-hb:hover,.btn-tw:hover,.btn-fb:hover,.btn-pk:hover,.btn-line:hover,.btn-total:hover { background-color: #ccc; } i.fa-hatena:before { content: "B!"; font-family: Verdana; font-weight: bold; } </style> </head> <body>
<!--↓↓ここにソーシャルボタンが表示される↓↓--> <div id="sns-counter"></div>
<script> window.addEventListener("load",function(){ const css=[ "//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.6.3/css/all.min.css" ];for(i in css){ let html=document.createElement("link");html.rel="stylesheet";html.href=css[i];async=true;document.head.appendChild(html);}});</script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
<!--↓↓ここが非同期でPHPを呼び出す部分↓↓--> <script type="text/javascript"> //ポケットのストック数を取得 function fetch_sns_count(url, selector) { var defer = jQuery.Deferred(); jQuery.ajax({ url: 'get_sns_button.php?targeturl=<?php echo urlencode("https://".$_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]); ?>', dataType:'text', timeout: 10000, //10sec }).done(function(res){ jQuery( selector ).html( res || 0 ); }).fail(function(){ jQuery( selector ).html('error;'); }); }
fetch_sns_count('<?php echo urlencode("https://".$_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);?>', '#sns-counter'); </script> <!--↑↑ここが非同期でPHPを呼び出す部分↑↑-->
</body> </html>
結果、遅くなった。
なぜか遅くなりました。まだ組み込みたいWEBページに埋め込んでいない状態ですが平均2.2秒。GTmetrixのWaterfallを見ると、確かに非同期で動作は確認できました。
ちょっと実際のWEBページに組み込んでいろいろなパターンをベンチマークしてみないとわからなくなってきました。