pjaxを導入してみた。

 defunkt/jquery-pjax - GitHub

 「次はpjaxですねー」とか書いてから数ヶ月過ぎていたりしますけども、先日jquery-pjaxを用いて当ページのpjax対応をやってみましたよ。ちなみに、上記のリンク先にも書いてありますけども、pjaxはpushStateとajaxを組み合わせた造語で、ajaxを用いて表示内容を書き換え、pushStateを用いてそれに対応するURLへと置き換えhistoryも辿れるようにする技術です。

 ajaxによるパフォーマンス改善の期待。#!(shebang、或はhashbang)を用いた場合のURLと表示内容の不一致の改善(permalinkと言えない。ツールにより取得できない)。googlebot用の_escaped_fragment_なURLへの対応が不必要。などがメリットとして上げられます。一方で、pushStateに対応しているブラウザでしか使えない(現状最大勢力であるIEさんでは不可です。モバイル系ブラウザも厳しい)。同一URLでajax用とフルアクセス用で表示内容が変わる(厳密に言って)。などがデメリットして上げられるでしょうか。対応ブラウザが限られる事は結構深刻で、shebangを使う手法ではほぼ全てのブラウザで機能していただけに残念です。でも、時が解決してくれるかもしれないですし、そもそもpjax自体がいつまでも有効な手法であるかもわかりません。

 そんなわけで、我らがgithubのdefunktさんが開発しているjquery-pjaxです。もちろんgithubでも使われておりますし、jQueryのplug-inとして作られておりますし、仕様的にもスタンダードになるのではないかと思います。動作的には、pjaxが使えるブラウザどうか確認し、ajaxでHTMLを取得し要素を書き換えるが、ajax時の通信の再にX-PJAXというリクエストヘッダーを付加する。pjaxが使えない場合は普通にリクエストを送信するという感じになっています。特別なリクエストヘッダーを付加してくれるのでサーバ側での対応が可能になります。あればpjax用の、なければ普通のHTMLを返せばいいわけです。

 使い方は適用したいaタグの要素にメソッドチェーンでpjax()を実行させるだけです。その際、書き換える要素のセレクタの文字列を引数で渡せます。あらかじめ適用したい要素にData属性でセレクタの文字列を仕込んでおく事もできます。ajaxで取得するURLも要素のhref属性から取得してくれるので手間なしです。しかも、内容を変更する要素にpjax:start、pjax:endという名前で前後に任意の処理を行うカスタムイベントも設定できます。さらに、実際のイベントはjQueryのlive()を用いて登録されるので、最初に実行してしまえば追加で取得したHTMLの中にpjaxを適用できるリンクがあってもそれに勝手にイベントを登録してくれるという便利仕様です。素晴らしい。

 data-pjax属性を持ったリンクに全てpjaxを適用するようにして、その際に変更される要素はIDがcontentのものとすると、

$('a[data-pjax]').pjax();
$('#content').bind('pjax:start',function(){
    任意の前処理
}).bind('pjax:end',function(){
    任意の後処理
});

 みたいになりましたよ。

 これで基本的には動くのですけど、何だか私の環境ではpjaxになったりならなかったりしました。同じリンクを辿った場合でもなったりならなかったり。おかしいなと思ってソースを眺めてみましたら(読んではいない、なぜなら……)原因発見、timeoutの数値が私の環境では小さ過ぎました。どうやら上手く動いてくれないときは、pjaxで実行し始めているのですけどtimeoutして再度普通のリクエストを投げている、そういった動きだったようです。「なにこのブルジョワ数値。うちのサーバさまはそんなレスポンスで動いてくれないんだよぉ」とガッツリそれなりに大きくしましたら安定動作するように。しょっぱいサーバでごめんなさいねぇ。悲しいです……。

 それと、送られてくる内容にhtmlタグを発見するとこれまた読み込み直してくれるので、最初にちょっと動かしてみようみたいなときに手を抜くと上手く動かせないみたいな事にもなります。ええ、なります、なりましたとも。

 これでクライアント側は終了ですけど、今度は内容を返すサーバ側の準備が必要になります。当ページではDjangoというPython製のFrameworkを使っていますが、そちらの方でもちょっとだけごにょったのでそれはまた次回。でも、Djangoに限ってしまえば、

 jacobian/django-pjax - GitHub

 というのが既にあります。大した事ではなかったし私は自前で適当に適当な事にするのが好きなのでやってしまいましたが、こちらを活用するのもいいと思います。

 実際に使ってみてかなり気に入っているpjaxですが、個人的にはshebangのやり方も嫌いではなくて、これからも場合によってはそちらを選択すべきだと思います。閉じたWebアプリとかだとそちらの方が対応ブラウザが拡がるし、fragmentURLへの対応も必要ないので手間もあまり変わりません。どちらにせよ、私はこういう風にシームレスに遷移していったり変更していったりするのが好きなんですよね。みんなもやるといいと思うよ。