はじめまして。佐藤竜之介(@tricknotes )と申します。本連載では、ユニークな特徴を持つJavaScriptフレームワークであるEmber.jsの仕組みと、実践での活用方法について解説させていただきます。
なぜEmber.jsか
ここ数年、ネイティブアプリケーションのような使い勝手を備えたWebサービスが増えています。筆者が利用しているサービスを例に挙げるとGmail , Pivotal Tracker , Idobata などがあります。これらはどれも画面遷移がなく一枚の画面上であらゆる操作を行うため、「 Webサイト」というよりは「アプリケーション」と表現する方が適切でしょう。このようなアプリケーションはシングルページアプリケーション(SPA)と呼ばれ、従来の画面遷移中心だったWebアプリケーションと区別されることがあります。
ただ、SPAの開発には特有の難しさがあります。それはデータと表示を常に一貫した状態に保つことやアプリケーションの「状態」を意識すること、オブジェクトのライフサイクルを考慮することです。これらの困難さを軽減するために、SPAを専門にサポートするフレームワークが注目を集めています。
こういったフレームワークはユーザインタフェースを取り扱うパターンである「MVC」にちなんで、「 MV* フレームワーク」( ※1 )と呼ばれることがあります。
本連載で紹介するEmber.jsも「MV* フレームワーク」のひとつです。
emberjs.com
Ember.jsって?
JavaScriptで開発をしていると「このコードいつも書くなぁ…」という状況に出くわすことはないでしょうか? 例えば、「 オブジェクトの状態を監視して適切にDOMに反映する」 「 DOMのイベントを受け取ってデータを更新する」 「 DOMの生成・破棄のタイミングでイベントをon/offする」などの処理です。Ember.jsはそういった決まりきった処理をよしなに取り扱ってくれるため、プログラマはアプリケーションの使い勝手や期待通りの機能を満たせているかなどの本質的な部分の開発に集中できます。
もちろん、Ember.js以外にもたくさんの選択肢があります。AngularJS やReact 、その他にもMV*フレームワークのサンプル集であるTodoMVC を見てみると、数多くのフレームワークやライブラリが存在していることがわかります。
Ember.jsを選ぶべき場面
では、私たちがEmber.jsを選ぶべきなのはどんな場面なのでしょうか?
一口に MV*フレームワークといっても、アプローチの仕方はそれぞれ異なります。Ember.jsを特徴付けるのは第一に「フルスタックである」ということが挙げられます。すなわち、リッチなアプリケーション開発のために必要な仕組みすべてを提供することをEmber.jsは目指しています。その結果として、Ember.jsの初期学習コストはその他の軽量なライブラリと比べると大きいと言えるでしょう。ただ、あなたの作ろうとしているアプリケーションがある程度の規模を持つならば、そのコストを差し引いてもEmber.jsの掲げる「高い生産性」の恩恵を十分に受けられることでしょう。しかしながら、素早いハックが求められる場面ならば他の選択肢を検討したほうがよいかもしれません。
本連載ではEmber.jsの機能についてひとつひとつサンプルを作りながら解説する予定なので、Ember.jsによる快適な開発を体感していただけると嬉しいです。
Ember.jsの歴史
ここからはEmber.jsに親しみを感じてもらえるよう、Ember.jsの誕生した背景を説明します。
SproutCoreの時代
Ember.jsはSproutCore というMV*フレームワークから派生する形で誕生しました。SproutCoreはMac OS X用のアプリケーションを作成するためのフレームワークであるCocoa の設計思想をJavaScriptで実現しようという方針のもと開発されました。2008年当時、GUIアプリケーションのアイデアをWeb開発に持ち込んだというのは大変画期的で、それまでDOM操作が主流だったJavaScriptの世界に本格的なアプリケーションを作るための可能性が広がりました。
ただ、Webアプリケーションの世界ではHTML+CSSという表現力豊かな画面作成の基盤がすでにあったので、独自のやりかたでGUIを組み立てるよりも既存のやり方をそのまま利用できるほうがメリットが大きかったのです。そのため、当時SproutCoreのコアメンバーであったYehuda Katz 氏はSproutCore 2.0を開発することを決意しました。
しかし既存の仕組みから大きく方針転換をするにあたって、単純にバージョンを上げるだけではその違いをアピールするのに不十分と感じたYehuda氏はフレームワークの名前を変えて再出発することを決めました。そうして開発が始まったのがEmber.jsです。2011年12月13日 のことでした[2] 。
Ember.js正式リリース
Ember.jsの開発がスタートしてから正式リリースまでの間およそ一年半は何度も試行錯誤が繰り返されましたが、2013年5月1日にリリースされたバージョン1.0.0 ではEmber.jsの方向性がしっかりと定まり、APIが安定したものになりました。
今現在ではYehuda Katz 氏、Tom Dale 氏を筆頭に多くのコミッターとEmber.jsコミュニティがその開発をサポートしています。
これからのEmber.js
バージョン1.0.0が出たからこれで完成、というわけではありません。本稿執筆時点での最新の安定版は1.8.1 で、APIの改善や機能追加が継続して行われています。
そしてAngularJSやReactといった他のライブラリの優れたところを学びつつ、またあるときは他のライブラリに影響を与えつつ[3] 、MV*フレームワークのエコシステムの中で成長しています。その中から生まれた新しいアイディアは、Ember.js 2.0に向けたロードマップとして公開されています[4] 。
2.0と言ってもAPIがまったく変わってしまうわけではなく、現在のAPIをより洗練させていままでより直感的な記述ができるよう進化を続けているところです。
Ember.jsの特徴
さて、Ember.jsのバックグラウンドについて理解が深まったところで、次はEmber.jsの特徴を解説します。
Ember.jsはRuby on Rails からCoC(Convention over Configuration; 設定より規約)の理念を取り入れており、適切な名前付けを行うことでEmber.js本体が各クラス同士を結びつけアプリケーションを組み立ててくれるようになっています。そのため自明なコードを省略することができ、少ないコードで見通しよくアプリケーションを構築することができます。
Ember.jsの機能
では、Ember.jsの特筆すべき機能を見ていきましょう。
Auto updating templates
Routing
Components
Auto updating templates
Ember.jsではHandlebars と呼ばれるHTMLに非常に近いテンプレート言語を使って画面を構築します。Handlebarsから生成されたHTMLでは、ユーザからの入力によってデータが変更されるとそのデータを表示している部分すべての表示が自動的に更新されます。この仕組みは双方向バインディングと呼ばれています。
Routing
Ember.jsでは画面にパーマリンクを割り当てることができます。パーマリンクを記録しておけば別のブラウザでアプリケーションを開いても同じ状態で表示することができます。また、別のユーザに特定のメニューを表示する手順を伝える際、"メニュー"->"設定"->"カスタマイズ"のような操作手順を伝えるのではなく、"http://example.com/settings/custom "のようなURLを伝えられる、ということが可能になります。
Components
HTML構造をまとめてひとまとめにして、独自の要素として扱うことができます。ComponentsはHTML構造だけでなく、イベントや内部実装をカプセル化します。例えば「"1分前"や"3日前"などの相対日付を表示するタグ」や「ストップウォッチを実現するタグ」を作成して、アプリケーションに埋め込むことができます。これはHTML5で仕様策定が進められているWeb Components にインスパイアされて導入された機能で、将来的にはWeb Componentsとの統合も視野に入れられています。
環境準備
さて、それではいよいよ実際にEmber.jsを動かしてみましょう。
Ember.jsと依存ライブラリをダウンロードします。
本稿執筆時点でのEmber.jsの安定版は1.8.1です[5] 。
ダウンロードしたら、これらをlibs
ディレクトリの下に置きます。
.
└── libs
├── ember.js
├── handlebars-v1.3.0.js
└── jquery-2.1.1.min.js
HTMLを用意します。
index.html
という名前で次の内容を保存します。
<!DOCTYPE html>
<html>
<head>
<script src= "libs/jquery-2.1.1.min.js" ></script>
<script src= "libs/handlebars-v1.3.0.js" ></script>
<script src= "libs/ember.js" ></script>
</head>
<body>
</body>
</html>
index.html
はlibs
と同じディレクトリに配置します。
.
├── index.html
└── libs
アプリケーションを初期化します。
app.js
という名前で次の内容を保存します。
App = Ember .Application .create ();
app.js
はindex.html
、libs
と同じディレクトリに配置します。
.
├── app.js
├── index.html
└── libs
index.html
を編集してbody
タグの次のように書き換えます。
<body>
<script src= "app.js" ></script>
</body>
Templateを用意します。
bodyタグの中に、次のコードを追記します。
<script type = "text/x-handlebars" >
< h1 > Hi , {{ name }}!</ h1 >
My name is {{ input value = name }}.
</script>
ここまでで、次のようなファイル構成になっているはずです。
.
├── app.js
├── index.html
└── libs
├── ember.js
├── handlebars-v.1.3.0.js
└── jquery-2.1.1.min.js
ではブラウザでindex.html
を開いてみましょう。テキストフィールドに入力した文字がリアルタイムで画面上に反映される画面が確認できたでしょうか?
name
というプロパティを介して、ユーザからの入力とHTMLへの表示が同期されているのがわかります。これがEmber.jsの機能であるAuto updating templatesです。
次の画面が完成イメージです。
Ember.js example
その他の環境構築方法
また、この方法以外にもbower やEmber CLI といったEmber.jsが推奨するビルドツールで環境を構築する方法もあります。詳しくは公式サイト をご参照ください。
また、手軽にEmber.jsを触ってみるというだけでしたらEmber.js用のjsbin を使うという方法もあります。
お好みの方法で環境を構築してください。
Ember.jsの構成要素
ここまでだと、「 動いているのはわかったんだけど、どうやって動いているのかわからない!」と感じている方が多いのではないでしょうか? それもそのはず、Ember.jsでは明示的に記述しなくてもいい部分が多いため裏側の仕組みがわかりづらいのです。そこで、ここからはEmber.jsの構成要素を紹介しつつEmber.jsが動作している仕組みを解説します。
今まで書いたコードで暗黙的に動いている部分は、明示的に書くこともできます。先ほどのサンプルと同等なアプリケーションを実現しようとすると、次のようなコードになります。
app.js
App = Ember . Application . create ();
App . Router . map ( function () {
this . resource ( 'index' , { path : '/' });
});
App . IndexRoute = Ember . Route . extend ({
model : function () {
var you = { name : null };
return you ;
},
controllerName : 'index' ,
viewName : 'index'
});
App . IndexController = Ember . ObjectController . extend ({
});
App . IndexView = Ember . View . extend ({
templateName : 'index'
});
また、テンプレートにもdata-templates-name
属性を追加します。
index.html
<script type = "text/x-handlebars" data-template-name = "index" >
< h1 > Hi {{ name }} from Ember . js </ h1 >
My name is {{ input value = name }}.
</script>
いきなりコードが増えましたね。では、これらの要素を例に挙げつつEmber.jsの構成についてみてみましょう。
Ember.jsのアーキテクチャ
Router … URLとRouteの対応付けを行います。
Route … Model, Controller, Viewの紐付けを行います。アプリケーションにパーマリンクを与える場合、その都度Routeを作成します。
Model … 画面に表示するモデルです。任意のJavaScriptオブジェクトを使うことができます。
Controller … Modelをデコレートして、ユーザからの入力イベントを処理します。また、アプリケーションの内部状態を保持する役割もあります。
View … DOMの操作やDOMからのイベントを扱います。
Template … Controller, ModelをHTMLに反映します。Handlebarsで記述します。
さきほどのサンプルの場合、URLの入力からHTMLの表示までは次のような流れで処理されます。
App.Router
がURLをもとにApp.IndexRoute
をアクティブにする
App.IndexRoute
はmodel
メソッドを実行して、画面に表示するモデルを取得する
App.IndexRoute
はcontrollerName
プロパティからApp.IndexController
を見つけ出しインスタンス化する
App.IndexRoute
はviewName
プロパティからApp.IndexView
を見つけ出しインスタンス化する
App.IndexView
はtemplateName
プロパティからdata-template-name="index"
のテンプレートを取得して表示に備える
App.IndexRoute
はmodel、controller, viewを結びつけ、templateに描画してHTMLを生成する
適切な名前付けによって自明なコードを省略することで、最初に作成したサンプルのようにとてもシンプルに記述することができます。
まとめ
今回はEmber.jsの歴史と特徴を紹介しました。次回からは簡単なアプリケーションを作成しながらEmber.jsの各機能について詳しく解説します。