Block Rockin’ Codes

back with another one of those block rockin' codes

Pjax みたいなことを Socket.IO + pushState でやってみる

本文

時間がないので走り書き備忘録。失敗エントリです。


Github で使われてから、defunkt/jquery-pjax · GitHub がにわかに話題になって、自分も面白いと思っています。
で、この処理自体は pjax を使うと非常に簡単に出来てしまうんですが、
中身を少し見ておきたかったのと、
やる事自体に意味が有るかどうかは別として、
それと同じようなことが WebSocket でもできないかと思って試してみました。
(リアルタイムな Web でもどっかで URI 振りたい時もなくはないし)

ajax と pushState

ざっくり説明すると。

最近のブラウザは

  • history.pushState
  • history.popState
  • history.replaceState

という API があってこれを使うとヒストリー(ブラウザの履歴)を JS から変更できます。
これを使うと、動的に更新した画面の中で URI を変更し、さらに戻る事もできるようになります。
pjax は ajax で画面を書き換えた時 URI を変更し、戻れるようにし、さらに X-PJAX を送る事で pushState が使えないブラウザでは通常の遷移ができるようにすることができます。

pushState + ajax

まず、 ajax で変更しながら URI を変化させ、戻るボタンで表示も戻るようにします。
URI は pushState で追加していき、戻るときは 'popstate' イベントが発生するので、
そこを補足し、画面を復元します。


'/1' をリクエストすると '1' を返すサーバを立てているとします。
以下のような画面で、リンクをクリックすると、表示内容が変わる感じ。
(お手軽に load() で。)

  <div id="area"></div>
  <a href="1" id="1">1</a>
  <a href="2" id="2">2</a>
  <a href="3" id="3">3</a>
$(function(){
  $('a').bind('click', function(e) {
    var id = e.target.id;
    $('#area').load('/' + id);
    history.pushState(id, id, id);
    return false;
  });

  $(window).bind("popstate", function(e){
    var id = location.pathname;
    log(id);
    if(id === '/') return false;
    $('#area').load(id);
  });
});


これでリンクをクリックすれば、 URI は変化し、
戻ると表示も戻ります。

pushState + socket.io

おなじみ socket.io でやってみます。
補足すべきイベントがわかったので、
その辺を置き換えるだけ。
サーバ側は手を抜いて、送られてきた数を返すだけです。

// Client
var socket = io.connect('http://localhost');

socket.on('connect', function() {
  log('connected');
});

$(function(){
  $('a').bind('click', function(e) {
    var id = e.target.id;
    socket.emit('send id', id);
    history.pushState(id, id, id);
  });

  socket.on('push data', function (data) {
    $('#area').text(data);
  });

  $(window).bind("popstate", function(e){
    var id = location.pathname.split('/').pop();
    if(id === '/') return false;
    socket.emit('send id', id);
    return false;
  });
});

サーバは部分的に(こちらも手抜き)

// Server
io.sockets.on('connection', function (socket) {
  socket.on('send id', function (id) {
    socket.emit('push data', id);
  });
  socket.on('disconnect', function() {
    log('disconnected');
  });
});


一応そんな感じになる。

問題

popstate が発生するのは「画面が遷移した後」なんですね。
ということで折角確立した WebSocket のセッションも切れてしまい、
戻るたびに再接続する必要がでます。


Ajax はステートレスだから「戻る」= 「画面遷移」なので問題ないですが、
WebSocket でそれが起こるとあまり意味がなくなってしまいます。


遷移しないと popstate が発生しない以上、これを防ぐ方法はないか。


中途半端な結果ですが、一応ソースはここです。

Jxck/socket.state · GitHub