pjaxが便利過ぎて鼻血出そうになった(railsのサンプル付き)
最近pjaxという単語をよく見るから調べてみたら結構よさげだったのでrailsを使ったサンプルを記事にしてみました。
pjaxとAjaxの違い:Ajax=技術、pjax=jQueryプラグイン
- Ajax
- JavaScriptを用いてサーバから非同期に取得したデータを用いてコンテンツを改変する技術。
- pushState
- HTML5で登場した、ブラウザの履歴を変更する仕様兼その実装。history.pushStateで使える
- pushState+Ajax
- Ajaxでコンテンツを変えると同時にpushStateでブラウザの履歴を更新して、さらに更新後のURLに直接アクセスしても同じページが表示されるようにする。つまり、Ajaxで変更した後のページにもパーマリンクを作る。その結果ブラウザの戻るボタンで、同一ページ内のAjaxの効果を消したりもできるようになる。
- pjax
- pushState+Ajaxを実現するjQueryプラグイン。https://github.com/defunkt/jquery-pjax
なので、pjaxとAjaxはレイヤーが異なる単語のようですね。
pjaxを使ってみる
pjaxを使うと、本当に簡単にpushState+AjaxなWebサービスを作れます
クライアント側の実装
サーバ側の実装
- リクエストヘッダにX-PJAX: trueがある、もしくはGETで_pjax=trueというのが送られているか調べる
- なかったら、html全体を返す
- あったら、コンテンツだけを返す
追記:下記は間違ってました
POSTで_pjax=true
railsで使ってみたサンプル
上記の仕様にそってrailsアプリケーションを作ってみた。
railsではレイアウトビューの中にyieldメソッドを組み込んでコンテンツを埋め込むので、pjaxの時だけレイアウトをオフにしてやることで簡単に実装できる。
追記:8月末日現在のpjaxの最新版にはバグがあって動作しません。http://pjax.heroku.com/jquery.pjax.jsをダウンロードしてくると、バージョンは古いですが動作します。下記のサンプルではこのjsを使っています。
- app/views/layouts/application.rhtml
<!DOCTYPE html> <html lang="ja"> <head> <%= javascript_include_tag "jquery" %> <%= javascript_include_tag "jquery.pjax" %> <script> $(function() { $("a.js-pjax").pjax("#main"); // #main の中身をAjaxで切り替える alert("hoge"); // 説明のためにつけただけ }); </script> </head> <body> <ul> <li><%= link_to("page A", { :action => "page_a" }, {:class => "js-pjax" }) %></li> <li><%= link_to("page B", { :action => "page_b" }, {:class => "js-pjax" }) %></li> </ul> <div id="main"><%= yield %></div> <!-- #main の中に yield を入れるのがみそ --> </body> </html>
- app/views/core/page_a.rhtml
<title>page_a</title> page_a
- app/views/core/page_b.rhtml
<title>page_b</title> page_b
- app/controllers/core_controller.rb
class CoreController < ApplicationController def page_a # pjaxだったらレイアウトを使わない。たったこれだけ render(:layout => false) if request.headers["X-PJAX"] end def page_b render(:layout => false) if request.headers["X-PJAX"] end end
動かしてみる
- 最初に/core/page_aにアクセスした
- レイアウトが有効なので読み込みが終わった時点でalert("hoge")が実行された
- 次にpage Bをクリックしてみた
- タイトルとコンテンツとURLが変更されている
- コンテンツだけを読み込んでいるので、alert("hoge")は実行されない
- 最後にpage Aをクリックした
- 最初のページに戻る
- コンテンツだけを読み込んでいるので、alert("hoge")は実行されない
この間のコンテンツの変更はAjaxで行われていますが、同時にpushStateしているので、ブラウザの戻るボタンを押せばpage_bのコンテンツが表示されます。
重要な点は/core/page_aに直接アクセスしても、page Aをクリックしても、同じURLで同じ内容が表示されている、つまりAjaxで動的に生成したページにパーマネントリンクが作られているということ。
なので、twitterやfacebookやはてブやメールで共有できるということですね。
しかも、Ajaxを使って必要最低限のものしかやり取りしないので、高速に動作する。
実装するのも非常に簡単なので、ユーザビリティー的にも、使わない手はないんじゃないでしょうか?