唐突に名前がかっこよさそうなJSフレームワークを触って遊ぶことにしました👏
Durandal.jsとは
MVCでもMVPでもMVVMでもなんでもござれみたいな感じで公式には書いてあるがぱっとみではMVVMっぽい(そもそもKnockoutJSがのってるわけだし)
フレームワーク自体の特徴はどんなものなのか、ということでその謳い文句をチラと読んでみるとこんなことが書いてある。
We didn't try to re-invent the wheel. Durandal is built on libs you know and love like jQuery, Knockout and RequireJS. There's little to learn and building apps feels comfortable and familiar. Dive in and enjoy.
どうやらKnockoutJS, jQuery, RequireJSと絡み合ってゴニョゴニョする
フレームワークらしい
調べてもあまり日本語の記事が出てこなかったので、国内ではあんまり有名じゃなかったのかな? と思ったり、Githubの最新コミットが大体一年前でフレームワークの開発自体はもうそんなに活発じゃなかったりで、いろいろ調べているうちに明らかにチョイスが間違えていることに気づき始めたけれど、もう後戻りする時間がないので今回はこの若干出落ち気味のJSフレームワークを紹介します👏
試す
bowerが使えるのでそこからインストールする。
$ mkdir durandal-example
$ cd durandal-example
$ bower install durandal knockout jquery requirejs --save
jQueryはDurandalの依存関係を無視して最新版をいれないと、バージョンが古いせいで意味の分からないエラーがでるので注意。
インストールが終わったらindex.htmlとエントリポイントになるmain.jsを用意する
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="applicationHost"></div>
<script src="bower_components/requirejs/require.js" data-main="js/main.js"></script>
</body>
</html>
requirejs.config({
paths: {
"text": "../bower_components/requirejs-text/text",
"durandal": "../bower_components/Durandal/js",
"plugins": "../bower_components/Durandal/js/plugins",
"knockout": "../bower_components/knockout.js/knockout",
"jquery": "../bower_components/jquery/dist/jquery"
}
});
define(function(require) {
var app = require("durandal/app");
app.title = "Durandal Example"
app.configurePlugins({
router: true,
dialog: true
});
app.start().then(function() {
app.setRoot('shell');
});
});
なにかルートになるモジュールを用意する。この場合はshell.jsがそれにあたる。モジュールに対応するビューは同じ名前を持つのでこの場合はshell.htmlになる(後述)
define(function(require) {
var app = require("durandal/app");
var ko = require("knockout");
var objects = {
name: ko.observable(),
sayHello: function() {
alert("Hello! " + this.name());
}
};
return objects;
});
<div>
<label>Name</label>
<input type="text" data-bind="value: name, valueupdate: 'afterkeydown'" />
<button type="submit" class="btn" data-bind="click: sayHello">Say!</button>
</div>
はい、できましたね。「Say!」と書いてあるボタンを押すとダイアログがでます。
モジュール
モジュールはRequireJSのdefine関数を使って定義できるもので、上の例ではshell.jsがこれにあたる。基本的にDurandal.jsではこのモジュールの組み合わせでアプリケーションを作っていく。
define(function(require) {
return {
...
};
});
定義されたモジュールのインスタンスは最初にrequireされるタイミングで生成され、常にシングルトンオブジェクトになる。
公式によれば、バックエンドサービスっぽいものを作りたいときはこんな感じでthisに生やしていけばよいとのこと。
define(function(require){
var backend = function(user, data){
this.user = user;
this.data = data;
};
backend.prototype.someRoutine = function(){
...
};
return backend;
});
各モジュールは必ずモジュールIDを持っていて、特にオプションを使わなければmain.jsを起点にした相対パスがモジュールIDになる。durandal/system
モジュールのgetModuleIdという関数を使っても、モジュールのインスタンスからIDを取得できる。
ビュー
- Durandal.jsでは各モジュールがそれぞれルートになるひとつのビューを持っていて、ビューとモジュールのファイル名は同じになる(
A.js
に対応するビューのファイルはA.html
、B.js
のときはB.html
みたいな感じ) - 異なった名前のビューとモジュールを紐付けたい時にはviewLocaterというモジュールを使うらしいのだけれど、色々長くなりそうなので今回はやめておく(リファレンスページ: http://durandaljs.com/documentation/View-Location)
- このデータバインディングの機構のところでKnockoutJSが使われている
コンポジション
Durandal.jsには、オブジェクトコンポジションとビジュアルコンポジションの2つがあり、後者がAngularJSのディレクティブ、Vue.jsのコンポーネントにあたるらしきもので、ビューを部分的に適用できる。
htmlだけをコンポジションとしてバインディングするのは非常にカンタン。
<div>
<h1>Header</h1>
</div>
<div>
<div data-bind="compose: 'header.html'"></div>
<div>body</body>
</div>
あるいは、モジュール単位でのバインディングをしたいときには、拡張子を含めずにモジュールIDを渡せば良い。
<div>
<div data-bind="compose: 'header'"></div>
<div>hoge</body>
</div>
この他にもビューとモジュールをそれぞれ別にひとつのコンポジションとしてバインディングする構文などいろいろあるらしいが、触れると厄介なことになりそうなのでここでは省略する。
Pub&Subモデル
durandal/events
というモジュールを使うと、イベント通知ができるようになる。特に説明すべきこともない名前通りの機能だが、違いとしてはイベントの送出がひとつの方向にしかないというところか。
app.trigger('customer:new', newCustomer);
app.on('customer:new').then(function(customer) {
...
});
イベントにall
を指定するとすべてのイベントをサブスクライブできる
app.on('all').then(function(event) {
...
});
プラグイン
ルーティングを紹介しようと思ったがいまいち直感的にわからなかったので力尽きた
http://durandaljs.com/documentation/Using-The-Router.html
所感
- Durandal.jsそんなに悪くなかった
- KnockoutJSを知ってるともっとよさそう
余談
Stackoverflowで見つけたこのスレッドによると、Durandal.jsの開発者はすでにAngular 2.0のチームに合流しているとのこと。また、Durandalの後継にあたるフレームワークはAureliaらしい。正直こっち触ればよかった。