引用元:
http://paiza.hatenablog.com/entry/2015/03/11/Backbone_JS%E3%81%8B%E3%82%89Angular2%E3%81%BE%E3%81%A7%E3%80%81%E5%85%A89%E5%A4%A7JavaScript%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%92%E6%9B%B8%E3%81%8D%E6%AF%94%E3%81%B9
(English article is
here.)

こんにちは、吉岡(@)です。
一体どれを使えばいいのか?何が違うのか?何から調べていいのか迷うことがあります。
そこで、現時点で事実上全てとなる、9大主要
フレームワークについて、実際に使ってみて比較を行います。
- Backbone.js
- Ember.js
- Knockout.js
- AngularJS(1.x)
- React.js
- Ractive.js
- vue.js
- Aurelia.js
- AngularJS2.0(アルファ版)
■サンプルコードの動作
書いたコードは苗字と名前を入れるとフルネームをその場に直ぐに表示するコードです。

単純なコードですが、これだけでも以下のような主要な動作を見ることができます。
■比較
フレームワークにも時代の流れがあり作られた時点の考え方を反映していますので、およそ作られた順番で書いていきます。
HTML:
First Name: <input id="firstName"/><br>
Last Name: <input id="lastName"/><br>
Full Name: <span id="fullName"></span><br>
function updateFullName(){
var fullName = $("#firstName").val() + " " + $("#lastName").val();
$("#fullName").text(fullName);
}
$("#firstName, #lastName").bind("change keyup", function(){
updateFullName();
});
$("#firstName").val("Taro");
$("#lastName").val("Yamada");
updateFullName();
jQueryを用いることで簡単に記述することができます。しかし、この方法には問題点があります。まず、”firstName”,”lastName”といった変数が
JavaScript上で、文字列として操作されており変更が難しくなっています。$()を用いた
jQueryオブジェクトも
グローバル変数のようになっており構造化が困難です。
一番の問題は、変数がHTML(DOM)上にしか存在しないとこです。そのため、データを構造化して処理することが難しくなっています。
◆1. Backbone.js
HTML:
<div id="person">
First Name: <input id="firstName" value=""><br>
Last Name: <input id="lastName" value=""><br>
Full Name: <span id="fullName"></span>
</div>
Person = Backbone.Model.extend({});
PersonView = Backbone.View.extend({
el: '#person',
events: {
'change': 'change',
},
initialize: function(){
this.listenTo(this.model, 'change', this.render);
this.render();
},
change: function(){
var firstName = $('#firstName').val();
var lastName = $('#lastName').val();
this.model.set({firstName: firstName, lastName: lastName});
},
render: function(){
this.$('#firstName').val(this.model.get('firstName'));
this.$('#lastName').val(this.model.get('lastName'));
var fullName = this.model.get('firstName')
+ ' ' + this.model.get('lastName');
this.$('#fullName').text(fullName);
},
});
person = new Person({lastName: "Yamada", firstName: "Taro"});
personView = new PersonView({model: person});
Backbone.jsはシンプルな
フレームワークで、ModelクラスとViewクラスが核となります。
HTML上の表現は、Viewを通じて行います。Viewは必要に応じて階層化されます。各ビューには対応するモデルがありますが、モデルとHTMLは直接関連することはありません。HTMLとビュークラスは
JQuery関数(val()/text())や、イベント監視を通じて連携します。ビュークラスとモデルクラスは、モデルのset/get関数やイベント監視(listenTo)を通じて連携します。
このように自動的に連携するような機能はなく、明示的に関連を作っていきます。凝った機能はありませんが、明示的に
疎結合を実現することで、大規模な開発にも耐えられます。
◆2. Ember.js
index.html:
<script type="text/x-handlebars" data-template-name="index">
First Name:{{input type="text" value=firstName}}<br/>
Last Name:{{input type="text" value=lastName}}<br/>
Full Name: {{model.fullName}}<br/>
</script>
App = Ember.Application.create();
App.Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
var person = App.Person.create({
firstName: "Taro",
lastName: "Yamada"
});
App.Router.map(function () {
});
App.IndexRoute = Ember.Route.extend({
model:function () {
return person;
}
});
Ember.jsでは、Backbone.jsでは明示的に記述する必要があった、HTMLと
JavaScript上のコントローラ、モデル上の変数の連携をデータ
バインディングにより自動的に行うことができます。
具体的には、”{{}}”という特殊記法を用いることで、
JavaScript上の変数とコントローラやモデルクラスの変数を直接結びつけます。
HTML上の入力用の変数の変更は自動的にモデル上に反映されます。モデル上の変更は、依存する変数(フルネームの場合、苗字・名前)をproperty関数で明示的に指定し、依存する変数に変更があった場合に自動的にHTML上の変数を更新します。
これりより、明示的にイベント処理を行うことなく、HTMLとモデル・コントローラが連携して動作します。
◆3. Knockout.js
HTML:
<p>First name:<input data-bind="value: firstName" /></p>
<p>Last name:<input data-bind="value: lastName" /></p>
<p>Full name:<span data-bind="text: fullName"></span></p>
function AppViewModel() {
this.firstName = ko.observable("Taro");
this.lastName = ko.observable("Yamada");
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
}
ko.applyBindings(new AppViewModel());
Ember.jsでは変数の依存性を依存先でまとめて書いていましたが、Knockout.jsでは監視対象の依存元変数を”observable”として宣言します。
具体的には、HTML上で”data-bind=“value: firstName””のように属性で変数を記述すことで、HTML上の変数がViewModel上の変数と対応することを示します。ViewModelクラスでは、入力変数は”ko.observable()”と明示的に指定することで変数の変更を検知します。変更があった場合、”ko.computed”で作られた変数が自動的に更新され、HTML上に反映されます。
◆4. AngularJS(1.x)
HTML:
<div ng-app ng-controller="PersonController">
First Name: <input type=text ng-model="firstName"> <br>
Last Name: <input type=text ng-model="lastName"><br>
Full Name: {{getFullName()}}
</div>
function PersonController($scope) {
$scope.firstName = "Taro";
$scope.lastName = "Yamada";
$scope.getFullName = function() {
return $scope.firstName + " " + $scope.lastName;
};
}
Ember.js、Knockout.jsによりHTMLと
JavaScriptのデータ
バインディングは自動的に実現されましたが、それでも依存関係は明示的に記述することが必要でしたが、AngularJSでは依存した変数の更新を自動的に行うことで、大幅に記述を簡略化できます。
HTML上の変数と
JavaScript上のコントローラを簡単に関連付けられることが特徴です。HTML上でng-model=“変数”や”{{}}”で表されるAngular式は、自動的にコントローラ上の変数と連携します。
JavaScript上では、明示的に変数の関連性を記述する必要はなく、通常の
JavaScript変数を用いてシンプルな記述が可能です。変数の監視は、何らかの操作が行われたあとに、すべての変数の変化を検出することで自動的に行われます。
◆5. React.js
var MyApp = React.createClass({
getInitialState: function(){
return {
firstName: this.props.firstName,
lastName: this.props.lastName,
}
},
handleChange: function(){
var firstName = this.refs.firstName.getDOMNode().value;
var lastName = this.refs.lastName.getDOMNode().value;
this.setState({
firstName: firstName,
lastName: lastName,
});
},
render: function() {
var fullName = this.state.firstName + this.state.lastName;
return (
<div>
First name: <input ref="firstName" onChange={this.handleChange} value={this.state.firstName}/><br/>
Last name: <input ref="lastName" onChange={this.handleChange} value={this.state.lastName}/><br/>
Full name: {fullName}
</div>);
}
});
React.render(<MyApp firstName="Taro" lastName="Yamada" />, document.body);
React.jsでは、表示するHTMLに対応するDOMの状態を
フレームワーク側で管理して自動的に変化の差分を検出します。これにより、開発者は明示的に状態の変化を管理する必要がなくなります。
具体的には、React.jsでは、描画するHTMLを
JavaScript上にJSX表記で直接埋め込みます。
埋め込まれたHTMLは直接HTML上に反映されるのではなく、VirtualDOMという形式で内部的に保持されます。
再描画が必要な変更がモデルにあった場合、setState()関数で通知します。
この時、React.jsはVirtualDOMの内容の変更点を監視し、変更された差分のみ実際のHTMLに反映させることで毎回全部描画しなおすことが必要なく高速化しています。
◆6. Ractive.js
HTML:
<script type="text/reactive" id="tpl">
First Name:<input type="text" value="{{firstName}}"/><br/>
Last Name:<input type="text" value="{{lastName}}"/><br/>
Full Name: {{fullName()}}<br/>
</script>
<div id='container'></div>
var ractive = new Ractive({
el: 'container',
template: '#tpl',
data: {
firstName: 'Taro',
lastName: 'Yamada',
fullName: function () {
return this.get( 'firstName' ) + ' ' + this.get( 'lastName' );
}
},
});
◆7. vue.js
HTML:
<div id="person">
First Name: <input v-model="firstName"><br/>
Last Name: <input v-model="lastName"><br/>
Full Name: {{fullName}}<br/>
</div>
var demo = new Vue({
el: '#person',
data: {
firstName: 'Taro',
lastName: 'Yamada',
},
computed: {
fullName: {
get: function(){
return this.firstName + ' ' + this.lastName;
}
}
},
})
Ractive.jsより更にHTML記述を自然に行えるようにし、よりHTMLを自然に書くことができるようになっています。
HTML中に”{{}}”またはv-modelで記述した変数が、
JavaScript側のモデル変数・関数と自動的に反映されます。
これ以上ないぐらいシンプルに記載でき、デザイン・プロトタイプ・小規模用途で使いやすくなっています。
◆8. Aurelia.js
app.html:
<template>
<section>
<form role="form">
First Name: <input type="text" value.bind="firstName"><br/>
Last Name: <input type="text" value.bind="lastName"><br/>
Full name: ${fullName}
</form>
</section>
</template>
app.js:
export class Welcome{
constructor(){
this.firstName = 'Taro';
this.lastName = 'Yamada';
}
get fullName(){
return `${this.firstName} ${this.lastName}`;
}
}
Aurelia.jsは、最新のECMAScript6/7の機能を使った、本格的かつシンプルな未来志向
フレームワークです。
Aurelia.jsでは、AngularJSで導入された豊富な機能(双方向
バインディング、ルーティング、
REST API呼び出し、DI)と、Ractive.js/vue.jsのシンプルさやパフォーマンスを、ES6/ES7のmoduleやObject.observe()等の機能を使うことで、同時に自然な形で実現しています。
今年中にも来るES6を見据えた最新の
フレームワークで、未来を先取りしている形ですが、ポリフィルを用いることでES6を実装していない現在のブラウザでも動作するようになっています。
HTMLのテンプレート記述もES6標準に沿っており”${}”、”value.bind”で変数を埋め込みます、
JavaScript上のクラスもES6で自然に記述でき、thisの変数やget/putによるプロパティで記述します。これらの変数が双方向に
バインディングされており、HTML/
JavaScript上の変数/関数が自動的に連携します。
Aurelia.jsについては、詳しくは以前書いた記事でも紹介しています。
◆9. AngularJS2.0(アルファ版)
app.html:
First Name: <input type=text [value]="firstName" #first (keyup)="firstNameChanged($event, first)"><br/>
Last Name: <input type=text [value]="lastName" #last (keyup)="lastNameChanged($event, last)"><br/>
Full Name: {{fullName}}
app.js:
import {Component, Template, bootstrap} from 'angular2/angular2';
@Component({
selector: 'my-app'
})
@Template({
url: 'app.html'
})
class MyAppComponent {
constructor() {
this.firstName = 'Taro';
this.lastName = 'Yamada';
this.updateFullname();
}
changed($event, el){
console.log("changes", this.name, el.value);
this.name = el.value;
}
updateFullname(){
this.fullName = this.firstName + " " + this.lastName;
}
firstNameChanged($event, first){
this.firstName = first.value;
this.updateFullname();
}
lastNameChanged($event, last){
this.lastName = last.value;
this.updateFullname();
}
}
bootstrap(MyAppComponent);
AngularJS2.0(アルファ版)は、先日のng-confで開発用サイトangular.ioがアナウンスされたばかりの現在開発中の
フレームワークです。
実際のリリースは1年程度先になると考えられますが、注目を浴びている
フレームワークですので、まだアルファ版ですが現時点での状況を元に紹介します。
AngularJS2.0(アルファ版)では、Aurelia.IOと同様にES6等の最新の
JavaScript規格を取り入れています。ES6に静的型検査や
アノテーション("@"記法)を追加した、TypeScriptをベースとしたAtScriptで記述します。これより、文法ミスの検出や
IDEとの連携が容易になりバグを減らすことができます。Angular1.xの売りでもあった双方向
バインディングは廃止されました。自動的な連携はできなくなりますが、より明示的に
JavaScriptとHTMLの動作を表現しやすくなり、動作がわかりやすくなり、ダイジェストループを無くしてパフォーマンスも向上します。
具体的には、@Component/@Templateにより
コンポーネントに対応する要素やテンプレートなどを指定します。要素名に対応した
JavaScriptクラスで動作を記述します。
ng-xxx記法が廃止され、代わりに"[式]"により
JavaScriptからHTMLへの連携、"(イベント)"によるHTMLから
JavaScriptへの連携、"#要素名"による要素の参照、というように記法が変更されています。scopeも廃止され、thisの変数を直接HTML上で参照できるようになります。
AngularJS2.0(アルファ版)については、詳しくは以前書いた記事でも紹介しています。
■まとめ
以上、現在の事実上ほぼ全ての9大主要
フレームワークについて出てきた流れに沿って書いてみました。
主な用途としては、Backbone.jsは大規模でカスタマイズして使う向け、Ember.js/Knockout.jsはより簡単にパフォーマンスを重視したプロジェクト向け、AngularJSは万能でどのプロジェクトでも、React.jsは簡単に
JavaScriptコードを書きたい場合、Ractive.js/vue.jsはHTML/デザイン中心にシンプルに使いたい場合、Aurealia.jsは今年中にも来るES6時代の将来性を見た場合、AngularJS2.0(アルファ版)は今後の動向を研究したい場合、になると思います。
paizaではスキルのあるエンジニアがきちんと評価されるようにし、技術を追い続ける事が仕事につながるようにする事で、日本のITエンジニアの地位向上を図っていければと考えています。特に
paizaでは
Webサービス提供企業などでもとめられる、
システム開発力や、テストケースを想定できるかの力(テストコードを書く力)などが問われる問題を出題しています。
テストの結果によりS,A,B,C,D,Eの6段階でランクが分かります。自分のプログラミングスキルを客観的に知りたいという方は是非チャレンジしてみてください。
0 件のコメント:
コメントを投稿