Angular Change Detection - How Does It Really Work?
- Angularの起動時にはlow-levelのAPIの上書きが行われている。例えば、addEventListenerを上書きすることで「ネイティブのaddEventListenerを実行→Angular独自の変更があるかどうか、あれば再描画する」という挙動にする。
- このPatchingを担当しているのがZone.js。Zone.js自体はシンプルに複数のJSのVMのターン(? どう訳すと適切かな…)に渡って生き残る実行コンテキスト。このように内部でChangeDetechtionを走らせるのに使われている。
- なので、IndexedDBなどのZone対象外のAsyncAPIではChangeDetechtionを走らせることが出来ない
- 各コンポーネントにはChangeDetechtorが作られて、テンプレート内で使われている変数が変わったかチェックしている。逆にいうと、例えばオブジェクトの中にテンプレートで使われていないプロパティがあったとして、その変更は追いかけない。(デフォルトの挙動)
- ところでなぜChangeDetectorのコードを共通しないのかというと、JavaScriptのVMに理由がある。動的なプロパティの比較はVMのJITコンパイラでネイティブコードに最適化することが難しいから。一方で、各コンポーネントごとのDetectorは私たちが普通に書くように直接プロパティにアクセスしている。結果として、この状態でも高いパフォーマンスを維持できる。
- もちろんコーナーケースはある。そういう時は
onPush
を使う。onPush
は@Input
の変化時のみしか再描画が走らない設定なので、例えば@Input
のオブジェクトを直接いじっても参照が変わらない限り再描画されない。だが一方で本当に@Input
の変化時のみしか再描画されないかというとそんなことはなくて、コンポーネントないのイベントが発火した時も走る。 - とはいえミュータブルなオブジェクトを扱うコンポーネントに大して使うと思わぬバグを起こしたりするので使いどころは見極める必要がある。