バージョンは1.8.1です。
(この記事書いてる間に最新バージョンが1.8.2になっててちょっとかなしかった。)
Marionetteにはソースコメントつきのコードが見れるとこがあるのです。
それを、こう。
普通にBackbone使うより良いらしいと聞くものの、実際にどんなもんなのかはわからず・・。
検索しても日本語の情報がほとんどないので、とりあえずソース読んでみようということで。
どうせ読むならコメントを日本語にしよう!と思い。
訳自体は直訳気味のものが多いので、ソース読んでみての学びも一緒にメモっておく。
Backbone.Marionetteとは
公式にも説明があるように、Backbone.jsをベースにしたライブラリで、
jsで大規模なアプリケーションを作る!ってケースに「よくある実装パターン」を集めたもの。
依存するライブラリ
Marionette └Backbone.BabySitter └Backbone.Wreqr └Backbone └underscore └jQuery
てな感じかな?
ばうわー いんすとーる まりおねっと すれば全部はいります。
Backbone.BabySitter
Backbone.Viewの親子関係を管理するコンテナを提供するライブラリ。
コードも150行くらいしかなくて、すぐに読めると思います。
// いつもどおりビューを定義して var SomeView = Backbone.View.extend({}); var AnotherView = Backbone.View.extend({}); // インスタンス作って var someView = new SomeView(); var anotherView = new AnotherView(); // BabySitterはBackbone.ChildViewContainerをエクスポートするので var container = new Backbone.ChildViewContainer(); // 管理をお任せする container.add(someView); container.add(anotherView);
コンテナにaddして、使うときはfindして、いらなくなったらremoveして・・みたいな。
それだけ。
Backbone.Wreqr
これはBackboneとMarionetteアプリケーションの橋渡しをするモジュール群。
Backbone.Eventsを継承したイベントのハンドラ、メッセージングの仕組みとか。
またも公式のコードを拝借すると、
var vent = new Backbone.Wreqr.EventAggregator(); vent.on("foo", function(){ console.log("foo event"); }); vent.trigger("foo"); // "foo event"
みたいなイベントハンドラさんや、
var commands = new Backbone.Wreqr.Commands(); commands.setHandler("foo", function(){ console.log("the foo command was executed"); }); commands.execute("foo"); // "the foo command was executed"
みたいなコマンドを固めておくやつ、
var reqres = new Backbone.Wreqr.RequestResponse(); reqres.setHandler("foo", function(){ return "foo requested. this is the response"; }); var result = reqres.request("foo"); console.log(result); // "foo requested. this is the response"
というようなリクエストに対してレスポンスする受け口を用意できる、と。
コードは割愛しますが、この3つをRadio/Channelって概念で分けて使えたりもする。
なんしか、Backbone.Eventsの拡張版ぽい。
Backbone.Marionette
このBabySitterとWreqrを使って、Backboneあるあるをふんだんに実装したのがMarionetteであると。
以下でくわしく。
Marionette
BabySitterとWreqrをそれなりに使ってどうこうする・・というよりは、
Marionetteとしても実装がだいぶ存在してて、むしろコードの行数でいうと、ほとんどがそうでした。
- BabySitterが150行
- Wreqrが400行
- Marionetteが2500行
まあ規模感の参考まで。
そもそもココで気付くべきやった。
2500行の実装にコメント打つとか作者がやるもんや・・。
Marionetteが提供する機能
ドキュメントは、公式のGithubにまとまってます。
参考:backbone.marionette/docs at master · marionettejs/backbone.marionette
でも多すぎて心折れるので、ざっと機能群をまとめておくと以下。
まとめの単位は私の独断と偏見です!
- ビュー
- リージョン
- ビヘイビア
- アプリケーション
- ルーター
- コールバック
日本語にするとコレジャナイ感が半端ない!
けど紛らわしいので無理やり押し通す。
ビュー
いわゆるビューで、4種類ある模様。
(ほんとは5種類で、全ての元となる"View"ってのがあるけど。)
- ItemView: 単一のモデル/コレクションを使うビュー(いわゆる子)
- CollectionView: コレクションを扱うビューで、イテレートするそれぞれはItemViewのインスタンスになる(いわゆる親)
- CompositeView: 上記2つをまとめて扱いたいときに使うビュー(いわゆる親戚)
- Layout: 後述するリージョンを内包するItemViewに準ずるビュー
それぞれその時々で最適なやつを使うべしとのこと。
コードすっきり
なんとBackbone.Viewでやってた、
renderで自身のelにテンプレをコンパイルしてデータとって・・の実装を書かなくても、これでOK。
// Marionette var MyView1 = Backbone.Marionette.ItemView.extend({ template: '#my-template' }); // Backbone var MyView2 = Backbone.View.extend({ template: '#my-template', render: function() { var inner = $(this.template).html(); this.$el.html(inner); return this; } });
その差は歴然ーというやつですね。
そのうえテンプレのコンパイルとかなんやかんやまとめてやっといてくれるイケメン。
ビューの定義時にまとめて設定できるものは他にもあるます。
参考:backbone.marionette/docs/marionette.view.md at master · marionettejs/backbone.marionette
@ui
これは便利ー。
MyView = Backbone.Marionette.ItemView.extend({ ui: { cat: ".dog", checkbox: "input[type=checkbox]" }, events: { "click @ui.cat": "bark" // "click .dog"したのと同じ }, onRender: function() { if (this.model.get('selected')) { this.ui.checkbox.addClass('checked'); // this.ui.でjQueryオブジェクトが取れるように } } });
Layoutってなんぞ問題
いい記事がありました!!
参考:backbone.js - What's the difference between a Marionette Layout and a Region? - Stack Overflow
リージョンってやつとの兼ね合いを押さえる必要があるっぽいですね。
リージョン
ビューと違ってテンプレートやDOMは持たず、自らに属するビュー達の表示/非表示など"管理"に徹するものぽい。
各ビューではなく、リージョン側で表示/非表示をコントロールすることで、ビューがゴテゴテになるのを防ぐことができる、と。
var myView = new MyView(); // showするとview.render()されて、DOMに落ちる MyApp.mainRegion.show(myView); // closeでDOMからさよなら MyApp.mainRegion.close();
そしてこのリージョンをも管理するリージョンマネージャーってのもあります。
ビヘイビア
つい最近のバージョンで追加されたそうな。
いろんなViewで似たような処理があった場合に、
「こいつら共通化できるやろ!・・けど継承するような関係ではないし、うーん。」
って時に使うといいよってな実装。
説明するより・・、
いい記事がありました!!!
アプリケーション
誰しもが作るであろうアプリケーションのオブジェクトです。
var MyApp = new Backbone.Marionette.Application(); // addInitializerで処理を積んでおいて MyApp.addInitializer(function(){ // ... }); // さらにあっちこっちで積んでおいて MyApp.addInitializer(function(){ new MyAppRouter(); Backbone.history.start(); }); // startしたら積んだ処理が随時走ってアプリ起動! MyApp.start();
で、この本丸はWreqrの機能も継承して持ってるので、
MyApp.vent.on('hoge', function(someData){ console.log('Received', someData); }); MyApp.vent.trigger('hoge', 'YEAHHHH'); // Recieved YEAHHH
ってなグローバルな用途にも使えて便利。
ルーター
Backbone.Routerより用途が明確になった感じかな?
var MyController = Marionette.Controller.extend({ doFoo: function() {}, doBar: function(id) {} }); var myController = new MyController(); var MyRouter = Marionette.AppRouter({ controller: myController, appRoutes: { "foo": "doFoo", // myController.doFoo のこと "bar/:id": "doBar" // myController.doBar のこと } }); new MyRouter();
さり気なく使ってみたけど、MarionetteにはControllerもあります。
コールバック
このカタカナのコレジャナイ感!
ようは、コールバックをよしなに貯めて実行できるキューみたいなのがあります。
var callbacks = new Backbone.Marionette.Callbacks(); callbacks.add(function(options){ alert("I'm a callback with " + options.value + "!"); }); callbacks.run({value: "options"}, someContext);
addしてaddして..さいごにrunする。
addする度にコンテキストを指定できるので、1つインスタンス作れば全部いけそう。
内部的には、初期化処理を貯めてるシーンが多かったです。
他にも内部では
ここで挙げた以外にも、Marionetteオブジェクトにはいろいろくっついてます。
御大へのリンク、置いておきます。
コード読んでみて
使い方
大枠を知りたい場合に、コードを一から全部読むのは時間かかるので、あまりオススメしないです。(普通はせーへんか
わかる人はサクッとをわかるんでしょうが(●・̆⍛・̆●)
バージョンはだいぶ古いですが、この動画はわかりやすいと思ったのでおすすめです。
参考:Tuts+ Premium Course: Advanced Backbone Patterns and Techniques - Backbone.Marionette
- アプリケーションつくる
- モデルとかコレクションとか用意する
- ビューとか用意する
- リージョンとしてビューをアプリケーションにくっつける
- いざ起動
わかりやすいですねー。
感想
Backboneの拡張性って偉大やな・・って思う程にBackboneを使い倒す気概が感じられるコードでした。
ただコードとしては読めるけど、なんでそういうコードになってるのかまで押さえないと使いこなせないなーという感じ。
基本的にMarionetteのクラスは全部extendできるようになってるとか、
onShow/onCloseなどなどライフサイクルが一定してて扱いやすい風とか、
なんだかんだBackbone.Eventsってすげーなって感じを受けました。
あと、複数人で書いてるからか、コードのスタイルとコメントのスタイルが統一されてないのが気になるw