« 2012年3月 | トップページ | 2013年1月 »

2012年12月

2012年12月31日 (月)

Backbone.js入門

はじめに

Backbone.jsのblog(Node.js + WebSocket + Backbone.jsのすすめ
Asset Pipelineのすすめ )を書いたのですが、あまり
反応がありませんでした。
理由を考えてみたのですが、blogの内容ではなく
そもそもBackbone.jsについてみんなよく知らない
のではないかと前向きにとらえることにしました。
そこでBackbone.jsを勉強しだして2ヶ月の私が、
JavaScriptはわかるけど、Backbone.jsはよく知ら
ないという人に向けてWebサーバをたてなくても
動作するサンプルを使って、手短に説明してみます。
そして次回はWebSocket(Socket.io)の連携について
説明してみます。

Backbone.jsが人気が出るようになった背景
Google Maps等、Ajaxを使い、クライアント側で動的
にページを更新することで、応答性が改善されました
が、サーバ側で部分的なHTMLを生成して、そのHTMLを
クライアント側でdivにAppendするといったことが行
われ、いつどこでこの部分を描画しているのかがわか
りにくくなりました。
また、スマートフォンの急速な拡大に伴い、モバイルも
重要なターゲットとなりました。

そこで、Ajaxによる複雑性を軽減し、さらにモバイル
でも使えるぐらい小さなライブラリが注目されること
になりました。

それがBackbone.jsです。

Backbone.jsはクライアント側にMVCを導入することで
プログラムの見通しをよくし、さらにコメントをあわ
せて1500行ぐらいの小ささのため、3G回線のモバイル
でも導入によるレスポンスの遅さをほとんど感じさせ
ません。※ただし、Backbone.jsは、jQuery(minimam
のzeptでも可)、underscore.js(コメント込みで1200
行程度)に依存します。

SPAについて
Backbone.jsを導入するとSPA(Single Page Application)
を意識してアプリを作成することになります。

SPAとは、その名の通り、一つ画面のみで構成されている
アプリのことです。

Webの場合は、最初のアクセスのみHTMLを取得して、以降
はAjaxやWebSocket等で必要なデータのみをやりとりし、
ページ遷移に対しては、HTMLをロードするのではなく、
JavaScriptでHTMLの内容を書き換えることで、素早い
ページの切り替えを行います。

Backbone.jsでは、SPAを支えるために大きく言って
RouterとModelとViewという3つに役割を導入しています。

Routerについて
RouterはPATHを監視し、PATHが変更されたら、その
PATHに登録された処理を呼び出します。
PATHの変更は、#(アンカー)が使われます。
アンカーはもともとページ内遷移に使うためのものですが、
Webサーバにアクセスしないという性質と、ブックマークが
できるという性質により、Backbone.jsにおいて、ページを
切り替える場合は、#以降のPATHを変更させます。
ブラウザがHTML5のPushStateが対応の場合は、Backbone.js
でPushState機能をOnにすれば、#ではなく変わりに実PATH
を使うこともできます。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
    <script type="text/javascript" src="https://raw.github.com/LearnBoost/socket.io-client/master/dist/socket.io.min.js"></script>
    <script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>
    <script type="text/javascript">
     var ExampleRouter = Backbone.Router.extend({
        routes: {
          "greeting/:language":   "greeting",
          ".*": "selecting"
        },
        greeting: function(language) {
          var msg = ""
          switch(language){
          case "spanish":
            msg = "!hola!";
            break;
          case "english":
            msg = "Hello!";
            break;
          default:
            msg = "ごめんやしておくれやしてごめんやっしゃー";
            break;
          }
          $('#contents').html(msg + '<br/><a href="#">back</a>');
        },
        selecting: function(){
          $('#contents').html(
          '<a href="#/greeting/spanish">Spanish</a><br/>' +
          '<a href="#/greeting/english">English</a><br/>' +
          '<a href="#/greeting/japanese">Japanese</a><br/>'
          );
        }
      });
      jQuery(function() {
        window.router = new ExampleRouter();
        Backbone.history.start();
       });
    </script>
  </head>
  <body>
    <div id="contents"></div>
  </body>
</html>
view raw route.html This Gist brought to you by GitHub.

上記のサンプルを任意の場所のファイルにコピーして
ブラウザでそのファイルにアクセスしてみてください。
(例) Macで/Users/takeshy/work/route.htmlという
名前でコピーした場合のURL。
file:///Users/takeshy/work/route.html

Routerのsampleについて
Routerは、Backbone.Routerを拡張します。routesの
プロパティに#以降のPATHをキーに、呼び出すメソッド
名の文字列を値としたHashを設定します。
そうすることで、Backbone.history.startのメソッドを
実行すると、#以降のPATHに応じて登録したメソッドを
実行してくれるようになります。
Pathに:名前を含めれば、(Sampleでは/greeting/:language
の部分)登録メソッドに対して:名前にあたる部分を引数
として渡すことができます。
また.*のように単に一致を判定したい場合は正規表現を
使えます。
Webサーバなしでページが遷移できることや、ブラウザの
戻る、進むボタンの対応や、特定のページをブックマーク
すると、ブックマークしたページの内容が表示されること
が確認できます。
※ここでは説明のためにRouteクラスをhtml内に記述して
いますが、普通は別jsに切り出します。

Modelについて
アプリの中心であるビジネスロジックのオブジェクト。
例えば、UserのModelだったら、名前やE-mail等の属性
を持ちます。
Modelのオブジェクトの集合を表わすCollectionと呼ば
れるクラスもあります。
Backbone.ModelやBackbone.Collectionを拡張することで
たくさんの機能が提供されます。

提供される機能

イベントのトリガー(Model & Collection)

Collectionの場合、内容を置き換えた場合はreset,
Modelのオブジェクトを追加した場合はadd、削除
した場合はremoveイベント等が発行されます。
Modelの場合、内容を変更した場合はchange,削除
した場合はdestoroyイベント等が発行されます。

データのSetter,Getter,validate(Modelのみ)

Modelオブジェクトに対して、defaults属性もしくは、
initialize({属性名:値})、もしくはset({属性名:値})
メソッドでセットした値は直接Modelオブジェクトの
属性にセットされるのではなく、attributesとよばれる
属性の中にセットされて、取得する場合は、get(属性名)
で取得できるようになります。
間接化することで、setメソッド実行によるchangeイベント
の発行やAjaxの送信にはattributesの内容のみを送信する
ので、Webサーバに送信しない属性を持つことができます。
validateメソッドを定義すれば、set時に値の妥当性を
チェックすることができ、不正時は、戻り値をセットする
ことで、errorイベントが発行され不正な値のセットを
防ぐことができます。

Ajax(Model & Collection)

RESTを前提として、save()やfetch()メソッドの実行で自動
的にAjaxでWebサーバにアクセスします。
ただし予めurl属性がセットされている必要があります。
Collectionのオブジェクトに対してfetchメソッドを呼びだ
すと、GET urlが実行されAjaxの戻り値の配列のJSONをmodel
の属性にもつModelオブジェクトに変換して、保持します。
IDがセットされたModelのオブジェクトに対してfetch()を
実行すると、GET urlRoot/IDの戻り値のJSONをModelの属性
にセットしします。またIDが未セットのModelのオブジェクト
にsave()を実行した場合、POST /urlRootのAjaxが実行され、
IDがセットされている状態でsaveするとPUT /urlRoot/IDの
Ajaxが実行され、destroy()を実行するとDELETE /urlRoot/ID
のAjaxが実行されます。

集合操作メソッド(Collectionのみ)

Collectionのオブジェクトに対して、each,map,findなど
underscore.js由来の便利メソッドがCollectionに対して
実行できるようになっています。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
    <script type="text/javascript" src="https://raw.github.com/LearnBoost/socket.io-client/master/dist/socket.io.min.js"></script>
    <script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>
    <script type="text/javascript">
      var ExampleModel = Backbone.Model.extend({
        defaults:{ name: "anonimous"},
        validate: function(attrs){
          if(attrs.age < 0){
            return "age must be positive";
          }
        }
      });
      var ExampleCollection = Backbone.Collection.extend({
        url: "/examples",
        model: ExampleModel
      });
      jQuery.ajax = function(params){
        console.log(params);
        params.success(
          [{id: 1,name: "takeshy",age: 36},{id: 2,name: "hoge",age: 22}]
        );
      };
      jQuery(function() {
        var sample = new ExampleModel({age: 0});
        console.log(sample);
        sample.on("error",function(obj,err){ console.log(err);});
        sample.set({age: -1})
        console.log(sample);
        var collection = new ExampleCollection();
        collection.fetch();
        console.log(collection.toArray());
        var model = collection.get(1)
        model.on("change",function(){ $("#contents").html(model.get("age"))});
        model.set("age",model.get("age")+1);
      });
    </script>
  </head>
  <body>
    <div id="contents"></div>
  </body>
</html>
view raw model.html This Gist brought to you by GitHub.

上記のサンプルを任意の場所のファイルにコピーして
ブラウザでそのファイルにアクセスしてみてください。
(例) Macで/Users/takeshy/work/model.htmlという
名前でコピーした場合のURL。
file:///Users/takeshy/work/model.html

Modelのsampleについて
Modelのnameに対してdefaultsの属性をセットしています。
このためブラウザのJavaScriptのコンソールを開くと、
new ExampleModel({age: 0})で作成したオブジェクト
のattributesのnameにデフォルト値がセットされています。
validateメソッドにageが0以上かどうかをチェックして
いるため、set({age: -1})の実行をするとerrorのイベント
に登録したconsole.log(err)により、コンソールに
"age must be positive."が表示されます。
また、console.log(sample)でageが0のままであること
が確認できます。
ExampleCollectionにはurl属性に/examplesをセットし
model属性にExampleModelをセットしているので、
ExampleCollectionのオブジェクトに対しfetch()を実行
すると、Backbone.jsの機能により、Get /examplesが
実行され、戻り値のJSONの配列をExampleModelの
オブジェクトの集合に変換するはずです。
今回はWebサーバなしのローカルファイルで実行できる
よう、jQueryのAjaxをダミー関数で上書きして、パラメタ
をconsole.logに出力しているので、type: GET,
url: /examplesが実行され、ダミーで返したJSONの配列
に基づいてExampleModelのオブジェクトが2個格納されて
いることを確認できます。
collection.get(ID)でcollectionに格納されているIDが
1のModelのオブジェクトを取り出し、そのModelに対して
changeのイベントをObserveして変更があれば変更になった
値を画面に表示するような関数を渡しているので、その後の
model.setによる変更により、画面上に37が出力されている
ことも確認できます。

Viewについて
RouterでPATHごとに処理を登録しますが、その処理の
中でViewオブジェクトを生成し、Viewに処理を移譲する
ことが、Backbone.jsでは一般的です。

Viewオブジェクトに対しては、下記を行います。

events属性に、HTML内のイベントに対して処理を登録。
(Form内で属性の値を変更した場合に変更内容をModel
に反映するetc)

Modelの更新イベントに対して処理を登録(Modelが変更
されたらrenderメソッドを呼ぶことで再描画するetc)

renderメソッドを呼び出されると、templateを
展開し、elと呼ばれるプロパティに描画すべきHTMLの
要素をセットし、自身を返す。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
    <script type="text/javascript" src="https://raw.github.com/LearnBoost/socket.io-client/master/dist/socket.io.min.js"></script>
    <script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script>
    <script type="text/javascript">
      jQuery(function() {
        //Model
        var CounterModel = Backbone.Model.extend({
          defaults: {counter: 0}
        });
 
        //View
        var ExampleView = Backbone.View.extend({
          //el: "<div/>", Backbone.jsのデフォルト
          template: _.template($("#sample_template").html()),
          events: {
            "click #ignore": "ignore"
          },
          initialize: function(){
            this.model = new CounterModel();
            //this.model.on("change",this.render,this);
            this.listenTo(this.model,"change",this.render);
            var self = this;
            window.setTimeout(
              function(){
                var counter = self.model.get("counter") + 1;
                self.model.set("counter",counter);
                console.log(counter);
                if(counter < 100){
                  window.setTimeout(arguments.callee,1000);
                }
              },1000);
          },
          render: function(){
            this.$el.html(this.template(this.model.toJSON()));
            return this;
          },
          ignore: function(){
            this.model.off("change");
          }
        });
 
        //Route
        var ExampleRouter = Backbone.Router.extend({
          routes: {
            ".*": "show"
          },
          show: function(){
            var view = new ExampleView();
            $("#contents").html(view.render().el);
          }
        });
 
        window.router = new ExampleRouter();
        Backbone.history.start();
      });
    </script>
  </head>
  <body>
    <script type="text/html" id="sample_template">
      <input type="button" id="ignore" value="無視" />
      <p> Counter is <%= counter %></p>
    </script>
    <div id="contents"></div>
  </body>
</html>
view raw view.html This Gist brought to you by GitHub.

上記のサンプルを任意の場所のファイルにコピーして
ブラウザでそのファイルにアクセスしてみてください。
(例) Macで/Users/takeshy/work/view.htmlという
名前でコピーした場合のURL。
file:///Users/takeshy/work/view.html

Viewのsampleについて
ViewはModelオブジェクトと連携するのが基本なので
counterという属性のみをもつModelを定義しています。

Viewの一番重要なelという属性は、backbone.jsが
デフォルトとして<div/>がセットしています。
もちろんそれ以外がよければ、el: '<ul id="hoge"></ul>'
のように値をセットします。
単にtag種別だけでよければ、elをセットせず、
tagName: "ul"とすれば、el: "<ul/>"とした場合と
同じことができます。

template属性には、templateメソッドを定義するのが
よくあるパターンです。
今回はunderscore.jsのtemplateメソッドを使用して
いますが、view内にHTML文字列を書いたり、元HTML
にtemplateを仕込むのは、保守を考えるとイケていない
ので、Assets Piplineを使うのがいいと思います。

events属性には、このview内のHTMLの要素に対する
イベントをキーにメソッド名の文字列を値にセット
しています。

Backbone.jsのオブジェクトは、初期時にinitialize
メソッドが呼び出されるので、initializeの中で、
Modelオブジェクトを生成し、Modelオブジェクトの
変更イベントに対して、renderメソッドを登録
しています。
これにより、modelの値をviewを意識することなく
変更することができ、変更によってrenderが呼ばれて
表示が更新されます。
今回はTimerを使ってmodelのcounter属性を1秒ごと
に100まで+1しています。
これにより1秒おきにviewが更新されているのが確認
できます。
また、今まではmodelの変更をmodel.onで登録していた
のですが、その場合、Viewが廃棄される前にmodel.off
を使ってイベントハンドラを解除しないとメモリリーク
してしまうという問題がありました。
最近ViewにListenToというメソッドが追加され、ListenTo
経由でイベントハンドラをセットするとView破棄時に
自動的にイベントハンドラが解除され、メモリリークが
防げるので、これからはListenToを使いましょう。

お決まりのrenderメソッドの中でtemplateメソッドに
template内で使う変数名と値が組になったHashを
渡します。Backbone.jsのModelオブジェクトに対して
toJSON()を呼びだすと、attributesの値のコピーが
渡されるのでそれを使うのがよくあるパターンです。
templateメソッドにより、elにhtmlの要素をセットし、
メソッドの最後でviewオブジェクト自身を返すのが
決まりとなっています。

無視のボタンを押すとignoreが呼ばれるようにevents
属性にセットしたのは、modelに対するイベントハンドラ
を削除することで、modelの値が変わっても、画面が
更新されないことを確認するためです。
consoleを見れば、modelの値は更新されつづけている
ことが確認できます。

最後のrouterですが、viewのrender()メソッドにより
viewのel属性に生成されたHTMLを実HTMLに反映する
のはRouterで行うことがidiomとなっています。

終りに
最後のサンプルでは、model,router,viewと組み合わせた
ので、このviewのサンプルが理解できたら、backbone.js
の基礎は理解できたことになります。

もしわかりにくかったり、間違っている箇所があれば、
教えてください。

Happy backbone!

| | コメント (0) | トラックバック (0)

2012年12月20日 (木)

Asset Pipelineのすすめ

Backbone.js Advent Calendarの20日目です。

Railsを使っている人なら知っているけど、他の言語の
フレームワークを使っている人は知らないかもしれない
Asset Pipelineについて書きます。

Railsは3.1からSprocketsと呼ばれるライブラリを使って、
Asset Pipelineという仕組みを導入しました。
初めは仕組みがややこしく、デフォルトで導入しなくても
よかったんじゃ?と思っていましたが、Backbone.jsを使う
ようになって、Backbone.jsユーザにとっては、神機能だと
いうことがわかるようになりました。
Node.jsにもmincerというSprocketsのクローンがあります。
(説明はまた後で)

それではここからAsset Pipelineって何、Backbone.jsに
とって何がうれしいの?を述べます。
Asset PipelineはManifestに従ってJavaScriptもしくはCSS
を生成するためのツールです。
Manifestとは下記のようにファイルの先頭にどの順番でどの
ファイルを展開するかを指定することです。
blog.js.coffee
#= require_self             <-自分自身を展開
#= require backbone  <- パス直下のbackbone.jsを展開
#= require_tree ./templates <- templates,blogsディレクトリ配下を
#= require_tree ./blogs   <- サブディレクトリも含めて再帰的に展開
window.Blog =
  Models: {}
  Collections: {}
  Routers: {}
  Views: {}

Manifestで指定されたファイル or ディレクトリ配下の
ファイルを展開し、ひとつのファイルにまとめます。
ひとつのファイルにまとめることで、Webブラウザから
アクセスするパスはひとつだけですむようになり、描画まで
の時間が大幅に短縮されます。
さらにSprocketsの場合は、minifyといって変数名を短縮
したり、余計な空白を削除することでファイルを小さくし、
さらに圧縮可能なブラウザ用に圧縮済みのファイルも用意
します。
Backbone.jsでSingle Page Applicationを作成した場合は、
開始時に大量のJavaScriptを読み込むため時間がかかりやすい
ですが、それを大幅に軽減してくれます。

まとめたファイル名にはフィンガープリントが末尾に付加
されており、内容が変わった場合は、ファイル名が変わる
ことでキャッシュがクリアされる仕組みになっています。

また開発時は、MiddleWare(RailsであればRack,Node.js
ではconnect)にマウントすれば、Webサーバを再起動せずに
ファイルの更新がクライアントへの応答に反映されます。
これは動的にコンパイルを行っているため負荷がかかりますが、
本番時にはその機能を無効にし、予めコンパイルした静的
ファイルを用意することでレスポンスの高速化を行うことが
できます。

JavaScriptのファイルを生成する場合は、元のファイルは
JavaScriptだけでなく、CoffeeScriptがあればそれを
コンパイルしてJavaScriptに変換したものを展開して
くれます。
CSSのファイルを生成する場合は、元のファイルがLESS、
SASS、Stylusだった場合はそれをコンパイルしてCSSを
生成してくれます。
ejsの拡張子があるものに関してはejsの評価結果をJavaScript
やCSSに反映してくれます。
これは環境変数によって設定を変えたJavaScriptやCSSを
用意したい場合に便利です。

さらにBackbone.jsユーザにとって嬉しいことは、JST
(JavaScriptテンプレート)に対応していることです。
これによりTemplateのHTMLを別ファイルに切り出す
ことができます。
11日目のAdvent CalendarでAjaxによって別ファイル
のTemplateを呼び出す方法が記載されていましたが、
Asset Piplelineを使えばコンパイルにより静的に
Viewファイルのtemplateが作成されます。
post_view.js
 Blog.Views.Posts ||= {}
class Blog.Views.Posts.PostView extends Backbone.View
  template: JST["templates/posts/post"]
  events:
    "click .destroy" : "destroy"
 
tagName: "tr"
 
destroy: () ->
   
@model.destroy()
   
return false
 
render: ->
    $
(@el).html(@template(@model.toJSON()))
   
return this
templates/post/post.jst.hamlc
%td= @title
%td= @content
%td
  %a{href: "#/#{@id}" ;} Show
%td
  %a{href: "#/#{@id}/edit"} Edit
%td
  %a{href: "#/#{ @id}/destroy< /b>",class: "destroy"} Destroy
JSTのTemplate元のフォーマットがSprocketsの場合
はEJSもしくはEcoですが、mincerの場合は Haml Coffee
もしくはJadeになります。
mincerのほうは癖があり、プログラではないデザイナさん
には大変ということと、Railsからの移行を考えるとEJSを
使いたいということもあり、拡張子がjst.ejsの場合はJST
展開する修正を行ったmincerを作りました。

SprocketsはRails標準ということもあり、ドキュメントが
たくさんあるのでここでは説明せず、あまり知られていない
mincerについて述べます。
以前紹介したサンプルアプリにもmincerが使われています。

本番環境(静的にコンパイルする)の場合

node_modules/mincer/bin/mincer.js -I app/assets/javascript -o public/assets   /絶対パス/app/assets/javascript/blogs.js.coffee

npm install mincerでnode_modules配下にmincerが
インストールされるのでそれを実行
-I で読み込む元のJavaScriptファイルがある場所
-o でできあがったファイルを置くディレクトリ
最後にコンパイルしたいManifestつきのファイル名を
絶対パスで指定します。
(私の環境では絶対パス以外なぜかうまくいかなかった。)

開発環境の場合
app.configure('development', function(){
  var Mincer  = require('mincer');
  var environment = new Mincer.Environment();
  environment.appendPath(__dirname + '/app/assets/javascript');
  app.use('/assets', Mincer.createServer(environment));
  app.use(express.errorHandler());
});
上記をメインのjs(app.js? or server.js)に記述すると、
/assets/ファイル(拡張子なし)で/app/assets/javascript配下の
ManifestつきのCoffeeScriptなりJavaScriptファイルを参照できる
ようになります。
Node.jsを再起動することなく、JavaScriptのファイルの変更が
反映されます。

Happy Backbone!!

| | コメント (0) | トラックバック (0)

2012年12月17日 (月)

Node.js + WebSocket + Backbone.jsのすすめ

Backbone.js Advent Calendarの16日目です。

新しい技術は興味はあるけど、主流にならない技術も多くて全部覚えるにはお腹いっぱいという皆さん。

私もこれはという技術を身につけて、一生食べて行けれたらと常々思っています。

そしてようやくこれはと思える技術に出会いました。

それが表題のNode.js+WebSocket+Backbone.jsです。

それを使った簡単なサンプルアプリ(backbone-railsのサンプルをrailsではなく

Node.jsに置き換え、DBをRedis、データの同期にSocket.ioを使ったもの)のソースは

ここにあります。アプリの解説はまた今度します。

今回はなぜこの組み合わせに賭ける気になったのか思ったを述べます。


利点
  1. デバイスを選ばない
  2. HTML5の利点でよく言われているようにPCはもちろん iPhone、Androidでも動作します。

  3. ネイティブ並に動作が速い
  4. Backbone.jsを用いてSingle Page Applicationとして作成するため、 ページ遷移ごとにServerにアクセスせず、JavaScriptによるページの 切り替えのため、一瞬で画面が遷移します。

  5. リアルタイム更新
  6. WebSocketを用いてBackbone.jsのModelを同期させれば、 Backbone.jsのView側でイベントを検知して表示を更新 してくれるので、実装側としては、勝手にリアルタイム 更新ができている感じです。

  7. Serverへの負荷が低い
  8. linkedInのモバイルの事例で見られるように、Node.jsは マシン効率がものすごくいいです。またWebSocketにより、 表示更新のための無駄なアクセスが必要がなくなります。

  9. 覚える言語がJavaScriptだけでいい
  10. 複数の言語を覚えるより、一つの言語に習熟してライブラリ、 関連ツールを覚えるほうが、開発効率は高いはず。 もちろん、CoffeeScriptやDart、TypeScriptなどJavaScriptに 変換できる言語も使えます。私はBackbone.jsと同じ作者が開発 したCoffeeScriptを愛用しています。

欠点
開発効率は現時点ではRailsのほうが圧倒的に速いです。
Node.jsの場合、DBに複数回アクセスする場合などcallbackが入れ子に
なり、ややこしくなります。
Backbone.jsの場合、Rest前提のため、複数のデータを一度に更新したい
場合や確認画面をはさみたい場合などにかなり頭を悩ませます。
テストの書きやすさやRepl機能の充実度もRailsのほうが上です。
Node.jsを勉強したてということもあり、実感としては1.5倍~倍近く
工数がかかっています。
開発工数も考えると、RESTサーバはRails等で開発したり、Single Page
Applicationにする箇所をよく使う機能に限定する等もありかもしれません。

まとめ

今までのプラットフォームは、言語こそ違えどできることはほとんど
変わらない中、Node.jsでは、他のプラットフォームでは真似できない
リアルタイム性やマシン性能を有効に活用できるほぼ唯一のプラットフォーム
だというのは今後も生き残る可能性大だと思います。
開発が難しいと言う点は、これから情報が増え、ツールが充実したり
することで徐々に改善されていくと思います。
そしてプログラマとして希少価値をあげるためには、難しいことも習得する
必要があることを考えたら、丁度いいと思いませんか?


追記

せっかくなので上記のサンプルアプリを公開してみました。

複数ブラウザでアクセスすると、瞬時にお互いの画面が更新されるのが
わかると思います。(WebSocketのおかげ)
ページの遷移もWebサーバにアクセスしないので一瞬です。
それにも関わらず、ブラウザの戻るに対応していたり、どの画面
もお気に入り登録して、その画面に直接飛べます。(Backbone.jsのおかげ)

| | コメント (0) | トラックバック (0)

« 2012年3月 | トップページ | 2013年1月 »