重く遅いいいねボタンを非同期化して素早くページを読み込む

CakePHPでソーシャルボタンを非同期読み込みにして表示速度を向上させる

いいねボタンの読み込みが遅いとページ全体のレンダリングに影響を及ぼす

いいねボタンをはじめとしたソーシャルボタンの設置方法を確認しながら、同期処理と非同期処理の違いを確認した上で、

最終的にソーシャルボタンを非同期化した状態で設置する方法をCakePHPで紹介するエントリーです。

同期タイプ(通常)のボタンを設置

最初に同期方式でソーシャルボタンを設置してみます。設置場所はCakePHPのWelcomeページ的な存在であるRelease Notesのページに設置してみます。編集対象のページは、

app\View\Pages\home.ctp

になります。

<h3>Social Button(同期)</h3>
<div class="actions">
	<div class='fb-like' data-href='http://social-button.local' data-send='false' data-layout='box_count' data-width='450' data-show-faces='true'></div>
	<div id="fb-root"></div>
	<script>(function(d, s, id) {
	  var js, fjs = d.getElementsByTagName(s)[0];
	  if (d.getElementById(id)) return;
	  js = d.createElement(s); js.id = id;
	  js.src = "//connect.facebook.net/ja_JP/all.js#xfbml=1";
	  fjs.parentNode.insertBefore(js, fjs);
	}(document, 'script', 'facebook-jssdk'));</script>
</div>
<div class="actions">
	<a href='https://twitter.com/share' class='twitter-share-button' data-count='vertical' data-via='xxxx' data-url='http://social-button.local' data-text='social-button'>Tweet</a><script type='text/javascript' src='//platform.twitter.com/widgets.js'></script>
</div>
<div class="actions">
	<script type='text/javascript' src='https://apis.google.com/js/plusone.js'></script><g:plusone size='tall' href='http://social-button.local'></g:plusone>
</div>

個々のソーシャルボタンをactionsというclass属性を付けたdivで囲むことでボタンが横一列に並ぶようにしました。このclass属性はCakePHPのデフォルトcssに設定されているスタイルになります。

表示してみると画像のような状態になります。

cakephp-socialbutton

水色で囲った部分が上のコードで新しく表示される部分になります。この状態を非同期化します。

Ajaxを利用してボタンを非同期化する手順

入れ物を用意

同期タイプの貼り付けたHTMLコードを以下のコードに置き換えます。

<h3>Social Button(非同期)</h3>
<aside id="social-button"></aside>

ソーシャルボタンの設置コードを削除して、ボタンを格納する入れ物だけを記述しておきます。id属性を追加しておくことで、非同期で取得したボタンコードの埋め込み先をAjaxリクエストで指定可能な状態にしています。

Actionの新設

ボタンのHTMLコードをAjaxで取得するために新しいアクションをコントローラ内に新設します。ボタンのHTMLコードだけをレスポンスするアクションを追加すると考えることもできます。まず、Ajaxリクエスト用に新しいルーティングを定義します。

// ↓標準のルーティングはコメントアウト
//Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));

// /ajaxでリクエスト可能にする
Router::connect(
    '/ajax',
    array(
        'controller' => 'pages',
        'action' => 'load'
    )
);

上で設定したようにloadアクションを新設します。新しく作成するといってもViewを返すだけのアクションを定義するだけです。

public function load() {
	$this->autoLayout = false;
}
Viewの作成

autoLayoutをfalseに設定することでページのレイアウト全体をレスポンスすることを回避して、View本体だけを返すようにします。そのViewにはソーシャルボタンのHTMLコードを記述します。

app\View\Pages\load.ctp

を作成します。

<div class="actions">
	<div class='fb-like' data-href='http://social-button.local' data-send='false' data-layout='box_count' data-width='450' data-show-faces='true'></div>
	<div id="fb-root"></div>
	<script>(function(d, s, id) {
	  var js, fjs = d.getElementsByTagName(s)[0];
	  if (d.getElementById(id)) return;
	  js = d.createElement(s); js.id = id;
	  js.src = "//connect.facebook.net/ja_JP/all.js#xfbml=1";
	  fjs.parentNode.insertBefore(js, fjs);
	}(document, 'script', 'facebook-jssdk'));</script>
</div>
<div class="actions">
	<a href='https://twitter.com/share' class='twitter-share-button' data-count='vertical' data-via='xxxx' data-url='http://social-button.local' data-text='social-button'>Tweet</a><script type='text/javascript' src='//platform.twitter.com/widgets.js'></script>
</div>
<div class="actions">
	<script type='text/javascript' src='https://apis.google.com/js/plusone.js'></script><g:plusone size='tall' href='http://social-button.local'></g:plusone>
</div>

ここまで作成すると

/ajax

にリクエストすることが可能になります。実際にリクエストするといいねボタンを先頭にソーシャルボタンのみが縦一列に表示されます。

Ajaxリクエストハンドラ―

この/ajaxへのリクエストで取得できる結果を先ほどの

<aside id="social-button"></aside>

に埋め込むのですが、その前に

/ajax

へのリクエストをajaxリクエストのみに限定し、ブラウザからのダイレクトアクセスに対しては、Not Foundを返すようにしておきます。そのためには、Ajaxリクエストハンドラ―でAjaxリクエストとその他のリクエストを判定可能な状態にします。

コントローラで、コンポーネントを読んでおきます。

public $components = array('RequestHandler');

アクションを以下のように変更し、ajaxリクエスト以外の場合は404エラーをレスポンスするようにします。

public function load() {

	if( !$this->request->is('ajax') )
	{
		throw new NotFoundException();
	}
	
	else
	{
		$this->autoLayout = false;
	}
}

CakephpでAjax controller編の記事では、Ajaxの判定とvalidationを組み合わせたやり方を書いてあるので参考にして頂きたいと思います。

Ajaxリクエストの送信

非同期でボタンのHTMLコードを取得するためには、フロント側からjQueryでAjaxリクエストを行います。jQueryのコードは、

app\View\Layouts\default.ctp

の/bodyの直前に追加しました。

<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js'></script>
<script type="text/javascript">
	jQuery(document).ready(function($) {

	    jQuery.ajax({
	        type: 'POST',
	        url: '/ajax',
	        dataType: 'html',
	        success: function(response){
	            $('#social-button').append(response);
	        },
	    });
	});
</script>

このコードは、

/ajax

にAjaxリクエストを送信し、

$('#social-button').append(response);

の部分でソーシャルボタン群を指定したタグ内に追加するコードになります。追加先として冒頭書いたsocial-buttonというid属性を指定しています。

ここまでの手順でボタンは非同期で表示されるようになります。

DOMContentLoaded eventとload event

ソーシャルボタンを非同期化することができたので、同期処理と非同期処理の違いを確認してみます。そのためには、ブラウザのjsインスペクタを利用します。今回はfirefoxのfirebugでネットタブ表示して双方のリクエスト状況を確認してみた。

同期(synchronous)

new-synchronous

非同期(asynchronous)

new-asynchronous

非同期の場合は、赤い縦のラインが同期処理のものよりも大きく左側にずれ込んでいるのが確認できる。ポイントとなるのは、青と赤のラインで、それぞれ、

  • DOMContentLoaded event
  • load event

を表している。

青のラインがDOMContentLoadedで、赤がloadである。

DOMContentLoadedは、ページ全体を読み込んだタイミングであるが、loadイベントは、ページ全体に依存するコンテンツの読み込みも完了したタイミングという点でDOMContentLoadedとは異なる。

今回の場合は、いいねボタンなどのソーシャルボタンが、ページ全体に依存するコンテンツに該当し、それらを非同期読み込みにすることで、loadイベントの外でリクエストを行うようにしており、その結果としてページの読み込み速度を向上させたということになる。

Webエンジニアブログにコメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

重く遅いいいねボタンを非同期化して素早くページを読み込むの記事にコメントを投稿