angularjsとfluxとriotjsと
TRANSCRIPT
AngularJSとFluxとRiotJSと
2015/10/28 GotandaJS # @mizuki_r
Profileaccount.
twitter: @mizuki_r github: ry_mizuki role.
フロントエンドエンジニア
tags.
AngularJS, RiotJS, Backbone,
hariko, angular-period, etc
Theme
• AngularJSで書いたアプリをFlux化した話
• Flux化したAngularJSアプリにRiotJS入れた話
AngularJSで書いたアプリをFlux化した話
What is Flux?
Flux’s architecture
https://facebook.github.io/flux/docs/overview.html
Flux’s architecture
• Facebookが提唱したアーキテクチャ
• データの流れを一方向に限定する
• Store, View, Dispatcherの責務がActionという単位で処理を回す
Flux’s architecture
https://github.com/facebook/flux/tree/master/examples/flux-todomvc/#structure-and-data-flow
Why Flux
Why Flux?• modelの管理に疲れた
• データ構造と責務分担の分類の考察
• 急なデータの変更、Viewの変更
• controllerでmodelを管理しがち
• modelとmodelの連携
• controllerの抽象化が雑に…
所謂MVCの運用には 高度な訓練と
高い意識が必要なのでは?
もっと低意識で運用したい!
I thought…
ぼくのかんがえたさいきょうの…• Dispatcherは自前でObserver定義 • ViewとActionCreator, Storeは一対
• ドメインの定義はViewにすべて移譲 • ActionCreatorは外部との連絡用
• API通信はngResourceをwrapしてrepositoryとして分離
• actionにする前にデータ整形を行うメソッドをかます
ぼくさいFlux arch
ぼくさいFlux arch
import * as action_creator from '../action-creators/example' import store from '../stores/example'
class ExampleCtrl { constructor ($scope) { this.onReceiveData = this.onReceiveData.bind(this) this.onDestroy = this.onDestroy.bind(this) store.addReceiveDataListener(this.onReceiveData) $scope.$on('$destroy', this.onDestroy) action_creator.init() } select () { action_creator.select() } onReceiveData () { this.data = store.getData() } onDestroy () { store.removeReceiveDataListener(this.onReceiveData) } }
angular.module('example').controller('ExampleCtrl', ExampleCtrl)
import dispatcher from '../dispatcher' import action_types from '../action-types' import injector from '../injector'
export function init () { dispatcher.handleViewAction({ type: action_types.INIT_EXAMPLE_VIEW, }) var Repository = injector.get('ExampleRepository') Repository.fetch().then((data) => { dispatcher.handleApiAction({ type: action_types.RECEIVE_EXAMPLE_DATA, data }) }) }
export function select () { dispatcher.handleViewAction({ type: action_types.SELECT_EXAMPLE_VIEW, }) var Repository = injector.get('ExampleRepository') Repository.post().then((data) => { dispatcher.handleApiAction({ type: action_types.POST_SELECT_EXAMPLE_DATA, data }) }) }
conclusion
• ViewをActionCreatorとStoreに抽象化できた
• コードが冗長になり、量が増えた
• 初期開発コストは高いが更新の手間などは減った
ViewをActionCreatorとStoreに抽象化できた
…あれ?
Angularじゃなくてもよくね?
Flux化したAngularJSアプリにRiotJS入れた話
What is RiotJS?
RiotJS
• Reactライクのユーザインタフェースライブラリ
• とっても小さい
• Virtual-DOM
• JavaScript in HTML
<todo>
<!-- layout --> <h3>{ opts.title }</h3>
<ul> <li each={ item, i in items }>{ item }</li> </ul>
<form onsubmit={ add }> <input> <button>Add #{ items.length + 1 }</button> </form>
<!-- logic --> <script> this.items = []
add(e) { var input = e.target[0] this.items.push(input.value) input.value = '' } </script>
</todo>
http://riotjs.com/ja/
ファイルサイズ
Reactの約1/19!
つまり…• 小さいし必要最小限の機能
• 細かいViewの制御なら十分なAPI
• 明示的な更新が可能なI/F
• Component系なので最小限の変更範囲
• 書きやすそうなJSX
Why is RiotJS?
AngularJSへの不満…
• $applyが重い
• 暗黙的な$digestの管理が辛い
$applyが遅い• 式を受け取りViewを更新するかを判定
• 更新する場合は`$scope.$digest()` を実行
• $http, $timeoutなどのAPIが内部的に利用
• 登録されているすべてのリスナを評価する
• 遅い評価式を登録してしまうと更新のたびに…
暗黙的な$digestが辛い• $http, $timeoutなどAPIが暗黙的に発行する
• 発行しないAPIもある
• 自前で作った機能は$scope.$apply等が必要
• たまたまうまく更新がされるケース
• digest中にdigestを発行すると死ぬ
つまり…• 特定のView以下で更新したい
• 短いサイクルでの更新
• 毎秒すべてのViewを評価してどうする
• 明示的に更新したい
• 必要ないところで評価されても
• 更新したいタイミングは僕らが決めたい
Angular with Riot
RiotJSの役割• 末端のViewの更新サイクルの管理
• 毎秒更新が発生するもの
• ホバーなどサイクルが短いもの
• ユーザアクションのハンドルと他のViewとの連携はFluxベースのアーキテクチャで吸収
• RiotからAngularのViewは参照しない
directive
angular.module('example', []).directive('todo', function () { return function () { riot.mount('todo') } })
directive化することで、templateから参照できる。
outside injector
exports.get = function (name) { return angular.element(document).injector().get(name) }
injectorをAngularの管理外から参照できるように
入れてみた結果• 「遅い」と言われてた部分が解消(体感的に)
• ぶっちゃけRiotJSが早いわけじゃない
• むしろ状況次第ではAngularより遅い
• $digest漏れや$applyの遅延による「遅く見える」が減少した
• RiotJS、以外と書きやすい
Conclusion
Conclusion• FluxによるViewの抽象化事例を紹介
• Viewの単位でコードを管理できるので、追加・更新・削除が思いの外簡単になった
• でもコード量は増えた
• AngularJSとRiotJSの共存事例を紹介
• 目に見えて反応が違った
• RiotJSは必要最小限で使う分にはかなり使い勝手良い
• 個人的にはRiotJSXはもっと使って行きたい
Next Generation• Fluxでの責務分担のあり方をもうちょっと考えたい
• いずれこれは二層式に集約されていくのだろうなと
• ServiceWorker上で扱えるレベルのロジックの抽象化
• メインスレッドを綺麗にしてUIのパフォーマンスあげたい
ご清聴ありがとうございました