backbone.png

はいどうも~。Japanese Schoolgirlsが好きなエンジニアの吉田です。


以前「jQuery.tmpl() で HTML の View と Model を分離してみるよ」という
記事を書きましたが、引き続きJavaScriptによるMVCシリーズ第二弾!

今回は、Backbone.jsでViewとModelを分離させてみるという試みです。

ここ最近、JavaScriptのMVCライブラリだと「JavaScriptMVC」や「Knockout.js
Backbone.js」などがぼちぼちと知名度を挙げてきているような印象です。

(※注)正確にはKnockout.jsはMVCではなくMVVM(Model-View-ViewModel)ですね。

そんな中SoundCloudなどでも使用されているBackbone.jsがとても気になって
いたので、どのような感じで利用できるのか試してみました。

ちなみに、Backbone.jsは有名どころだと以下のサイトで採用されているようです。
 
SoundCloud Mobile
LinkedIn Mobile
Basecamp Mobile
Groupon Now!


というわけで、早速Backbone.jsと戯れてみましょう!



・・・と、その前に、Backbone.jsの全体的な仕組みの把握が必要ですね。
 

全体像


Backbone.jsは、一言で説明するとクライアントサイドJavaScriptのMVCフレームワークという位置づけです。
(フレームワークというほど大がかりなものではないかもしれませんが。)
なんとなくGoogle Maps V3のMVC Objectに雰囲気が似ている感じがします。

Backbone.jsの大まかな仕組みを把握するために、主要となる概念を確認してみましょう。

Backbone.jsでは「モデル」「ビュー」「イベント」という3つの主要な概念が存在します。
イベント駆動なアプリケーション開発経験があれば、なんとなくイメージがつくでしょうか。

「モデル」はデータ操作を担当し、データの生成、バリデーション、破棄、
サーバサイドへのデータ保存などを行うことが可能です。

「モデル」のデータが変更された場合は「イベント」が発行され「ビュー」へ
通知される仕組みになっています。

「ビュー」はUIを担当し、「モデル」からの「イベント」を受け取ったのち、
「モデル」のデータを描画することが可能です。

なお、描画の際は、DOMから"id"属性を指定してHTMLを手動で更新する必要はありません。
「モデル」が変更された際に「ビュー」が自動的にそれらの表示を更新します。

 

インストール


backbone.jsを読み込むとすぐに使用可能です。以下のサイトからダウンロードできます。
http://documentcloud.github.com/backbone/

なお、Underscore.jsも必要なのでこちらも以下のサイトからダウンロードしてください。
http://documentcloud.github.com/underscore/
※Underscore.jsはJSでRubyっぽいメソッドなどが使えるようになるユーティリティライブラリです。
 

実践


今回もjsdo.itを使ってTwitterAPIから検索して表示させることにしてみましょう。

ボタンクリックでTwitterからデータを取得して、「モデル」にツイートデータをぶちこむようにし、
あとは「ビュー」にお任せして表示を更新させてみます。

ちなみに、BackboneのViewではDOMセレクタとしてjQueryやZepto.jsと一緒に組み合わせることが
できますが、今回はjQueryを使います。テンプレートエンジンも前回同様jQuery.tmpl()です。
※JSのテンプレートエンジンは最近だとICanHaz.jsなどでもいいかもしれませんね。


クラスの構造の方針は、以下のような感じで考えてみます。

・TweetModelクラス:個々のツイートのデータを扱う「モデル」
・TweetCollectionクラス:上記ツイートのコレクションの「モデル」
・TweetView:個々のツイートの「ビュー」
・ApplicationModelクラス:アプリケーションで使用する「モデル」
・ApplicationViewクラス:アプリケーションの「ビュー」



// ツイートのモデル
var TweetModel = Backbone.Model.extend({
    initialize: function () {
        this.set({
            domId: 'tweet_' + this.cid
        })
    }
});

モデルはBackbone.Model.extend({}) で作成することができます。
ここではinitializeメソッドで初期化処理をしています。
cidはBackboneが自動的に割り振るユニークなidで、DOMのIDとして使うと便利です。


// ツイートのコレクション
var TweetCollection = Backbone.Collection.extend({
    model: TweetModel,
});

コレクションのモデルを作成するにはBackbone.Collection.extend({})を使用します。
ここでは、先ほどのTweetModelのコレクションを定義しています。


// ツイートのビュー
var TweetView = Backbone.View.extend({
    render: function () {
        this.el = $("#tweetTemplate").tmpl( this.model.toJSON(), this );
        return this;
    },
    time_ago: function(time){
        var ints = {
            second: 1,
            minute: 60,
            hour: 3600,
            day: 86400,
            week: 604800,
            month: 2592000,
            year: 31536000
        };
        time = +new Date(time);
        var gap = ((+new Date()) - time) / 1000,
            amount, measure;
        for (var i in ints) {
            if (gap > ints[i]) { measure = i; }
        }
        amount = gap / ints[measure];
        amount = gap > ints.day ? (Math.round(amount * 100) / 100) : Math.round(amount);
        amount += ' ' + measure + (amount > 1 ? 's' : '') + ' ago';
        return amount || 0;        
    }
});

ビューはモデルのデータをUIへ伝える役割を果たします。

renderメソッドはデフォルトだと何も処理内容が定義されていませんが、
オーバーライドすることでモデルのデータをもとにビューのテンプレートを
レンダリングすることができます。

上記クラスでは、Twitterの ○秒前 という表示整形メソッドもViewに定義しています。


ここまでで、Tweetに関するViewとModelの定義ができました。
次に、これらを動かすためのメインアプリケーション用のViewとModelを用意します。


// アプリケーションのモデル
var ApplicationModel = Backbone.Model.extend({
    initialize: function () {
        this.tweets = new TweetCollection();
    }
});

// アプリケーションのビュー
var ApplicationView = Backbone.View.extend({
    initialize: function () {
        this.model.tweets.bind('add', this.addTweet);
        this.model.tweets.bind('remove', this.removeTweet);
    },    
    render: function () {
        this.el = $( "#tweetContainer" );
        return this;
    },
    addTweet: function (tweet) {
        var tModel = new TweetModel(tweet);
        var tView = new TweetView({model: tModel});
        this.render().el.append(tView.render().el);
    },
    removeTweet: function (tweet) {
        $('#' + tweet.get('domId')).remove();
    }    
});

こんな感じでメイン処理を受け持つアプリケーションクラスを作成しました。
このアプリケーションのモデルとアプリケーションのビューを実行することで
アプリケーションが動きます。


// アプリケーションの実行
(function(){     var appModel = new ApplicationModel({});     appView =  new ApplicationView({model: appModel}); })();


これで、アプリケーションが実行されるようになりました。

あとは、Twitterからデータを取得した際にコールバック処理で

   appView.addTweet(result);

という形でツイートデータを追加してあげれば、ツイートデータが
モデルに追加され、UIにも反映されるようになります。


jsdo.itで実際に動くものを作ってみたので以下に貼り付けます。

Backbone.js を使ってTwitterから検索 - jsdo.it - share JavaScript, HTML5 and CSS


※時間をあまりかけられなかったので、バグがあるかもしれません。。



まとめ


Backbone.jsを使うことで、ModelとViewをより疎結合にすることができました。

しかし、今回のサンプルでもわかるように、コードの量がそれなりに増えてしまうため、
規模の小さいアプリケーションには適していないのではないかと考えています。

JavaScriptでMVC構造を綺麗に保った状態でアプリケーションを開発する手法は
まだまだ情報が乏しいような容共なので、模索していく余地がありそうですね。

今回のサンプルプログラムもBackboneのとても簡単な使い方しかしていないので、
もっといいプログラムの書き方があると思います。
なので、参考程度にとどめて頂けると幸いです。
 

参考サイト

Building a single page app with Backbone.js, underscore.js and jQuery
http://andyet.net/blog/2010/oct/29/building-a-single-page-app-with-backbonejs-undersc/



なお、護身のため補足しておきますが、冒頭で述べたJapanese Schoolgirlsは
そういう趣味なわけではなく、Suspect 44というアーティストのJapanese Schoolgirls
というTranceの曲名なので、白い目で見ないでくださいね。。。


デワデワ。