Speeding up AngularJS @konpyu! html5minute! 2014.8.22
自己紹介 • KON Yuichi (@konpyu)! • Software Engineer in peace of cake! • Love Angular! • SNS note: AngularでClient構築
Frontend MV*
TodoMVC Benchmark http://vuejs.org/perf/
そもそも遅い
なぜか
2way data binding ViewModel View ・js が保持するデータと HTML として画面に表示さ れる内容が双方向で自動的に連携 ・大規模なSPA, WebAppで実装コストを削減可
2way data binding よくあるFW → 専用のクラスを継承して手動でBindingを設定 AngularJS → PureなJavascript ObjectをViewModelとして扱う $scope ↑この方
2way data binding <p>{{ user.name }}</p> html
2way data binding <p>{{ user.name }}</p> $scope.user.name = “konpyu”; html js
2way data binding 楽すぎる! <p>{{ user.name }}</p> $scope.user.name = “konpyu”; html js
2way data binding html <input type=“text” ng-model=“name”>
2way data binding html <input type=“text” ng-model=“name”>
2way data binding 楽すぎる! <input type=“text” ng-model=“name”> alert($scope.name); html js
Dirty Checking $scopeはPureなjavascript object → 値に何らかの変更があったかは言語の機能では検出 できない → Object.observe() はよ! ! Angularでは… 特定のタイミングで$scopeに変更があったか否かを $scopeの内容をすべて前後比較してチェック → digest loopと呼ぶ
なので、方針としては 1) 1回のdigest loopにかかる時間を減らそう 2) digest loopが起こる回数を減らそう
dirty checkingの対象を減らそう 変更あったで 変更あったで digest loop 対象が2000を超えると遅延が目立つと言われている http://stackoverflow.com/questions/9682092/databinding-in-angularjs/9693933#9693933
dirty checkingの対象を減らそう 変更あったで 変更あったで digest loop 対象が2000を超えると遅延が目立つと言われている http://stackoverflow.com/questions/9682092/databinding-in-angularjs/9693933#9693933
one-time binding version 1.3では値の変更が無さそうなものは最初の1回 しか評価しないように設定できる ! 余計な前後比較の数を減らせるので処理時間が減る - Official Doc https://docs.angularjs.org/guide/expression#one-time-binding
one-time binding
one-time binding 変わる可能性アリ 変わる可能性アリ
one-time binding 多分変わらない 多分変わらない 多分変わらない
one-time binding one-time bindingにしたい変数の前に::を付けるだけ
bindonce ・one-time bindingは1.3での実装 ・1.2系でも、同様のコンセプトのDirectiveがいくつか 公開されている ・Pasvaz/bindonce  https://github.com/Pasvaz/bindonce
重たいFilter ・Filterは便利だが、digest loopごとに計算されるので 重たい処理を挟むと時間がかかる ・Custom Filterを作る時は実行時間を意識する ・どうしようもなければ事前計算する
digest loopが起こる回数を 減らそう digest loopが起こるタイミング ! 1) DOMイベントが発生した時 2) $html,$resourceでレスポンスが返ってきた時 3) $locationで遷移が行われる時 4) $timeoutで指定秒が経った時
$timeoutは必要? ・$timeoutはsetTimeoutのwrapper ・setTimeoutとの違いは、tick時にdigest loopが走るか ・tick時に$scopeの変更がないのであれば、setTimeoutにした 方が余計なdigest loopが走らない ・もしくは、$timeoutの第三引数にfalseをsetするとdigest loop が走らない ← (例)10s後にPVを加算するAPIを call。tick時にDOMの書き換えは無 くdigest loopを走らせる必要がな いので第三引数をfalseにしてskip
API Callはなるべく1本に ・当たり前といえば当たり前だが… ・$http, $resourceでデータをfetchした時にdigest loop が走るため、画面構築時に何本もAPIを呼ぶとdigest loop が多く走ってしまう
まとめ ・Angularの2way data bindingはPureなJS Objectが ViewModelになります ・PureなJS Objectに変化があったか否かは現状のjsの仕様では 検知できないので、無理やり前後比較をします(Dirty Checking) ・watch対象が増えるとdigest loopにかかる時間が増えて重く なります ・digest loopにかかる時間と、発生頻度を減らす事が高速化の 基本方針です

AngularJSの高速化