MVCフレームワークの比較
JavaScriptのMVCフレームワークはAngularJS以外にもいくつかあります。例えば、Backbone.jsはAngularJSと共に人気があるフレームワークで、これ以外にもKnowkout.jsなどがあります。
ここでは、ソースコードを眺めて、それぞれのフレームワークの雰囲気を感じてみてください。
AngularJS
AngularJSはGoogleなども開発に参加しており非常に活発なオープンソースです。大きな特徴としては、「HTMLをそのままテンプレートとして使える」「双方向データバインディング」「独自タグ、属性」があります。
HTMLをそのままテンプレートとして使え、HTMLに"{{変数名}}"と記述すればそこに値が差し込まれます。HTMLのDOM構造を意識せずにJavaScriptのコードを記述することができます。リスト1は、フォームで入力した文字をそのまま<span />タグ内に表示する際のHTMLの例です。このように、jQueryのセレクタ記述などをせずにHTMLに値を設定することができますので、後から比較的容易にHTMLの構造を変更することができます。また、HTMLにはng-***で始まる属性がありますが、これらはAngularJSで使える拡張機能で多数の機能があります。これらの機能は今後紹介していきますが、このHTML拡張機能によりAngularJSは今後も多くの機能が追加されていくことでしょう。
<script type="text/javascript"> function personContoller($scope){ // ng-controllerで指定 $scope.person = { // (1) モデルの定義( ng-model や {{変数}} で利用 ) firstName: "初期値" }; $scope.check = function(){ // (2) 関数の定義 ( ng-clickで利用 ) alert($scope.person.firstName); }; } </script> </head> <body ng-app> <form ng-controller="personContoller"> <!-- (3) モデルで定義した変数を利用する --> <input type="text" ng-model="person.firstName"> <span>{{person.firstName}}</span> <button ng-click="check()">Check</button> </form> </body>
「双方向データバインディング」という聞き慣れない機能はどのようなものでしょうか?この機能を積極的に使っていた他の言語としては、Adobe Flexがあり、リッチクライアントを作成する言語として便利な機能です。
例えば、リスト1の(1)で定義したモデルはHTMLでの(3)のinputで入力した値がすぐにspanタグに反映されます。また、その値は同時にJavaScript内の変数にも反映され、(2)の関数内でもその値が利用できます。
このようにHTMLに変数を設定できるだけでなく、HTMLからも自動的に値を取得できるので「双方向」となるわけです。また、HTMLに独自タグを作成することができます。この機能を使ってHTMLにはないタグを独自に作成したり、Bootstrapなどを使ってHTMLを記述する場合にはUI Bootstrapを使って分かりやすいタグで記述できます。
素早く、短いコード量で多くのことを実現したい場合に向いているフレームワークです。一方デメリットとしては多くの機能がHTMLのタグや要素として提供されているので、それらを調べながら開発する必要があります。ただし、これらは今後リファレンス本などが出版されていけば解決する問題と思いますし、本稿でもそれらの機能を紹介していきます。
Knockout.js
リスト2(Knockout.jsでの記述例)と先ほどのリスト1(AngularJSでの記述例)を見比べても分かるように、非常に似たコードで同じことを実現できます。また、特徴も「HTMLをそのままテンプレートとして使える」「双方向データバインディング」にあります。従って、この2つのサンプルコードだけではどちらがどのような特徴を持っているかが分かりにくいと思いますが、変数をHTML側から使えるようにするに(1)のような指定が必要であったり、その値の取得には(2)のように関数になります。このように、AngularJSでは隠蔽して自動で行っているところを、意図的に指定する必要があります。
<script type="text/javascript"> var personViewModel = function(){ this.firstName = ko.observable('初期値'); // (1) バインディング対象にしている処理 this.check = function(){ alert(this.firstName()); // (2) 値の取得は変数ではなく、関数 } }; window.addEventListener('load',function(){ ko.applyBindings(personViewModel,document.getElementById('#person')); }); </script> </head> <body> <form id="person" data-bind="submit: check"> <input type="text" data-bind="value: firstName, valueUpdate: 'keyup'"> <span data-bind="text: firstName"></span> <button type="submit">Check</button> </form> </body>
AngularJSでは、「独自タグ、属性」などの特徴もありましたがKnockout.jsにはありません。代わりに、AngularJSよりもきめ細かいデータバインディングの記述がしやすくなっています。
Backbone.js
Backbone.jsの特徴はコーディング構造を整備し、再利用性のあるコードを記述しやすくするためのMVCの枠組みの基本的な部分に注力して提供しています。特にModel、Collection(Modelの集合)、Viewを分けることを強く意識して記述する必要があります。このため、AngularJSやKnockout.jsのように現場ですぐに使える便利な機能のようなものは標準では提供していません。同じような記述方法で実際に同じような機能を実装すると、先の2つのコードと比べてもコード量が多くなってしまい、リスト4のようになってしまいます。
<script> // Viewの作成(HTMLを操作する部分) var PersonView = Backbone.View.extend({ // (1) DOMに関するイベントとメソッドの関係を定義すると関数が自動的に呼ばれる events: { 'submit' : 'check', 'keyup input.model-firstName' : 'changeValue' }, // Backboneで実行される初期化処理 initialize: function(opts){ this.model = opts['model']; // (2) モデル(firstName)が変わったときに実行されるようにする this.model.on('change:firstName',this.updateView,this); }, // モデルが変更されたときに実行する関数 changeValue : function(evt){ this.model.set({ firstName: $(evt.currentTarget).val() }); }, // モデルの値(firstName)が変わったときに実行される関数 updateView: function(evt){ this.$el.find('span.firstName').text(this.model.get('firstName')); }, // 最初の画面表示 render: function(){ this.$el.find('input.model-firstName').val(this.model.get('firstName')); this.updateView(); }, // submitされたときに実行される check : function(evt){ this.model.check(); return false; } }); // モデルの作成 var PersonModel = Backbone.Model.extend({ defaults: { firstName: '初期値' }, check : function(){ alert(this.get('firstName')); } }); // モデルとビューを実際に利用する $(function() { var model = new PersonModel(); var view = new PersonView({ el: '#person', model: model }); view.render(); }); </script> </head> <body> <form id="person"> <input type="text" value="" class="model-firstName"> <span class="firstName"></span> <button type="submit">Check</button> </form> </body>
AngularJSや、Knockout.jsの「双方向データバインディング」に対して「イベントモデル」を採用しています。ここでは、(1)DOMイベントに対するCallback処理の定義、(2)モデルの値が変わったときに実行されるCallback処理の定義をするようにしています。サンプルのような処理ではモデルをわざわざ作成する必要もないかとは思いますが、Backbone.jsを本格的に利用する場合には、イベント駆動型になるためにこのようなコードに結局なってしまいます。
コードが長くなりがちなデメリットがありますが、モデルとビューがイベントを通じた粗結合のために、再利用性が高いコードを保つことが容易です。
この辺りは多少、サーバサイド側で生じた「Ruby(on Rails)」か、「Java(Servlet)」での開発がよいのかという議論に似ている気がします。Backbone.jsは前者の例えでいえば「Java」側の文化になると思います。
従って、このようなオブジェクト指向言語をやってきた開発者ならば、なじみやすいフレームワークです。また、足りない機能はプラグインを使って足すこともできます。例えば、データバインディングを使いたい場合には、stickitというプラグインも利用できますし、knockback.jsというようにknockout.jsとbackbone.jsを組み合わせて使うライブラリもあります。
おすすめのフレームワークは?
Backbone.jsではHTMLテンプレートやデータバインディング、Knockout.jsでは中大規模向けの管理の仕組みなどがAngularJSに比べて足りないと言えます。従って、他のライブラリなどで補完するという作業が必要になりますが、より使いやすい組み合わせを求めると複数のフレームワークに関する知識や、利用しているフレームワーク自体のある程度深い理解が必要になってしまいます。
それに比べて、AngularJSはフルスタック(他のライブラリなどに頼らない)のフレームワークと言えます。従って、どれを使ったらよいか分からないというケースや、初めてフレームワークに使ってみるという方におすすめなのがAngularJSです。また、自動的なデータバインディングや独自タグや属性によって、フレームワークの機能をすべて利用しなくてもそのメリットを受けることもできますので、使い初めてすぐに便利さが分かると思います。