僕はIncrements(このQiitaの会社)に入社して以来、KobitoのWindows版を作っていて、その中枢の画面遷移と状態管理をライブラリとして抜き出した。それがArda。
基本的には昨日発表した https://speakerdeck.com/mizchi/real-world-virtual-dom というスライドに書いたとおり。
Ardaの概要
Ardaの目的はFluxの概念をベースに、画面遷移と状態とシーンをベースにしたヒストリ管理、その際のDispatcherのコンテキスト切り替えを行うことを主な目的としている。
CoffeeScriptでの最小構成だと次のようなコードになる
Arda = require 'arda'
# Arda.Component extends React.Component
class HelloComponent extends Arda.Component
render: ->
React.createElement 'h1', {}, name: 'Hello Arda'
# Arda.Context
class HelloContext extends Arda.Context
@component: HelloComponent
window.addEventListener 'DOMContentLoaded', ->
router = new Arda.Router(Arda.DefaultLayout, document.body)
router.pushContext(HelloContext, {}).then ->
console.log 'push context done'
router.pushContextで画面を切り替えている。この画面遷移のあとにさらにpushContext(), popContextで現在の別のコンテキストに遷移することができる。基本的にあらゆる処理をPromise化できるように作っている(promisify)
由来
アルダは指輪物語の中つ国を含む世界そのものであり、シルマリルの物語によれば現実の私達の世界そのものでもある。仮想DOMを扱うので仮想と仮想を繋ぐ概念なのでそういうニュアンスを込めた…ということにしたが単に短くてユニークな名前が欲しかっただけである。最近のJSもうだいたいの名前空間潰れてて辛い。
開発の動機
既存のFluxフレームワークではあまり複数画面や画面遷移というもを意識したものが多くない。そもそもFluxの枠内にそういう概念を含んでいない。(react-routerは使いにくい)
Kobito AtomShell版の開発にあたり、最初にFluxish.tsという名前で、Kobitoの画面遷移の処理を部分を書いていたのだが、ここの処理が分厚くなり、Overworldという名前でOSSとして切り出した。(命名の理由は同上)
(これのAPIが安定した頃に一旦スクラッチで書き直したのがArda)
OSSにした意図は、僕自身がどうしてもソロ作業になりがちで、あわよくばちゃんと使われて一般的な知見のレベルにまで押しあげたいそうすれば属人化しにくい)という意図があった。業務ドメインが一切含まれていないことも大きい。
Overworldは次の特長がある。
- 一つのFluxのStore-Dispatcher-Viewの塊をSceneと名付ける
- Sceneは次へ(pushScene)、前へ(popScene)へ遷移できる
- Sceneの内部プロパティはProps(初期化子) - State(ミュータブルな状態) - TemplateProps(実際にViewに渡されるプロパティ)の三段階に分離
- ミュータブルな状態(State)に副作用を及ぼす処理の局所化と、型による保護
Overworldの問題
メタフレームワークをTypeScriptで記述することの恩恵の少なさ
元にしたFluxish.tsを引き継いでTypeScriptで書き続けていたのだが、抽象度が高いフレームワークの記述では型による旨味が少なかった。そのくせ型に制約された処理が多く、フレームワークのAPIを歪めていた。
Overworldを作る過程で、結果としてTypeScriptコンパイラが生成するのは全く別の型定義ファイルを作った。
これは結果としてArdaのAPIの精錬化に役に立っている
Ardaでの更新点
ArdaはOverworldを思想的に継承したものであり、加えて次の特長がある
- APIはOverworldほぼそのままに、用語を統一(Scene => Context)
- テストで実用に耐えうるレベルまでAPIを保護する
- CoffeeScriptでとにかくシンプルに
- Reactv 0.13.0-beta.1から可能なES6 classes を用いたクラスベースの設計
- ReactのwithContextの使用
- Contextが子のContextをとれる(別の画面を自分自身に含められる)
かなりドッグフーディングしててて、あんまり不安定な挙動残ってないと思う。(正直GC周りはちょっと不安だが…)
参考にしたもの
- omcljs/om
- scttnlsn/syphon
- Android のライフサイクルイベント
Ardaの始め方
examples以下に頻出パターンを書いてる
arda/examples at master · mizchi/arda https://github.com/mizchi/arda/tree/master/examples
また、僕が業務で作っている Kobito on AtomShell の、業務ドメインを除いた構成を、そのまま次のプロジェクトとして抜き出した。若干怪しい感じだが参考になると思う。gulpfileでもみてくれ。テストも走る。
mizchi-sandbox/arda-starter-project
- react
- react-jade
- coffee
- typescript
- gulp
こんな感じ
質問・要望・意見がある方はIssuesまで
Issues · mizchi/arda までどうぞ
あとで echo.js にポストしたいんだがGithubPagesに投稿するようのランディングページでも作ろうと思う。