角度変化の検出とOnPush戦略
公開: 2022-03-11お気に入りのすべてのプロジェクトでAngularの使用を開始しました。 Angularが提供するものと、それを活用してすばらしいWebアプリを構築する方法を知っています。 ただし、Angularには特定の点があり、それらを知っていると、プロジェクトでAngularをより適切に使用できるようになります。
データフローはAngularのほぼすべての中心にあり、変更検出は、バグをはるかに簡単に追跡し、複雑なデータセットを操作するときにアプリをさらに最適化する機会を提供するため、知っておく価値のあるものです。
この記事では、Angularがデータ構造の変更を検出する方法と、Angularの変更検出戦略を最大限に活用するためにそれらを不変にする方法を学習します。
Angularでの変化検出
モデルのいずれかを変更すると、Angularは変更を検出し、すぐにビューを更新します。 これはAngularでの変更検出です。 このメカニズムの目的は、基になるビューが常に対応するモデルと同期していることを確認することです。 Angularのこのコア機能は、フレームワークを目立たせるものであり、Angularが最新のWebアプリを開発するための優れた選択肢である理由の1つです。
Angularのモデルは、次のいずれかのシナリオの結果として変更される可能性があります。
DOMイベント(クリック、ホバーなど)
AJAXリクエスト
タイマー(setTimer()、setInterval())
変更検出器
すべてのAngularアプリは、コンポーネントの階層ツリーで構成されています。 実行時に、Angularはツリー内のコンポーネントごとに個別の変更検出器クラスを作成します。これにより、最終的にはコンポーネントの階層ツリーと同様の変更検出器の階層が形成されます。
変更検出がトリガーされるたびに、Angularは変更検出器のこのツリーをウォークダウンして、変更が報告されているかどうかを判断します。
変更検出サイクルは、検出された変更ごとに常に1回実行され、ルート変更検出器から始まり、順番に下がっていきます。 コンポーネントデータはその親からのみ取得できることがわかっているため、予測可能な方法でモデルが更新されるため、この順次設計の選択は優れています。
変更検出機能は、変更をAngularに報告するために、コンポーネントの以前の状態と現在の状態、およびその構造を追跡する方法を提供します。
Angularが変更検出機能からレポートを取得すると、対応するコンポーネントに、それに応じてDOMを再レンダリングして更新するように指示します。
変化検出戦略
値と参照型
変更検出戦略とは何か、なぜそれが機能するのかを理解するには、まずJavaScriptの値型と参照型の違いを理解する必要があります。 これがどのように機能するかをすでに理解している場合は、このセクションをスキップできます。
開始するには、値型と参照型、およびそれらの分類を確認しましょう。
値型
ブール値
ヌル
未定義
番号
ストリング
簡単にするために、これらのタイプは単にスタックメモリに値を格納することを想像できます(これは技術的には正しくありませんが、この記事では十分です)。 たとえば、下の画像のスタックメモリとその値を参照してください。
参照型
配列
オブジェクト
関数
これらのタイプは、ヒープメモリ上の実際の値を指す参照をスタックメモリに格納するため、少し複雑になります。 以下の例の画像で、スタックメモリとヒープメモリがどのように連携するかを確認できます。 スタックメモリがヒープメモリ内の参照型の実際の値を参照していることがわかります。
値型と参照型を区別する重要な点は、値型の値を読み取るには、スタックメモリにクエリを実行するだけですが、参照型の値を読み取るには、最初にスタックメモリをクエリして参照を取得し、次にその参照を使用してヒープメモリをクエリし、参照型の値を見つけます。
デフォルト戦略
前に述べたように、Angularはモデルの変更を監視して、すべての変更を確実にキャッチします。 アプリケーションモデル全体の以前の状態と現在の状態の違いをチェックします。
Angularがデフォルトの変更検出戦略で尋ねる質問は次のとおりです。モデルの値は変更されましたか? ただし、参照型の場合は、より良い質問をすることができるように戦略を実装できます。 これがOnPush変更検出戦略の出番です。
OnPush戦略
OnPush戦略の背後にある主な考え方は、参照型を不変オブジェクトとして扱うと、値がはるかに速く変更されたかどうかを検出できるという認識から明らかになります。 参照型が不変の場合、これは、更新されるたびに、スタックメモリ上の参照を変更する必要があることを意味します。 これで、簡単に確認できます。参照タイプの(スタック内の)参照が変更されましたか? はいの場合は、(ヒープ上の)すべての値を確認してください。 これが紛らわしい場合は、前のスタックヒープ図に戻って参照してください。

OnPush戦略は、基本的に1つではなく2つの質問をします。 参照タイプの参照は変更されましたか? はいの場合、ヒープメモリの値を変更しましたか?
たとえば、30個の要素を持つ不変の配列があり、変更があるかどうかを知りたいとします。 不変配列を更新するには、その参照(スタック上)が変更されている必要があることがわかっています。 これは、配列への参照が異なるかどうかを最初に確認できることを意味します。これにより、どの要素が異なるかを判断するために(ヒープ内で)さらに30回のチェックを行う必要がなくなる可能性があります。 これはOnPush戦略と呼ばれます。
では、参照型を不変として扱うとはどういう意味ですか? これは、参照型のプロパティを設定するのではなく、値をまとめて再割り当てすることを意味します。 下記参照:
オブジェクトを可変として扱う:
static mutable() { var before = {foo: "bar"}; var current = before; current.foo = "hello"; console.log(before === current); // => true }
オブジェクトを不変として扱う:
static mutable() { var before = {foo: "bar"}; var current = before; current = {foo "hello"}; console.log(before === current); // => false }
上記の例では、慣例により参照型を不変として「処理」しているため、最終的には変更可能なオブジェクトを処理しますが、それらが不変であると「偽る」だけであることに注意してください。
では、コンポーネントにOnPush戦略をどのように実装しますか? あなたがする必要があるのは、@ComponentアノテーションにchangeDetection
パラメーターを追加することだけです。
import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.OnPush }) export class OnPushComponent { // ... }
Immutable.js
AngularコンポーネントでOnPush戦略を使用することにした場合は、不変性を適用することをお勧めします。 そこでImmutable.jsが登場します。
Immutable.jsは、JavaScriptの不変性のためにFacebookによって作成されたライブラリです。 リスト、マップ、スタックなど、多くの不変のデータ構造があります。 この記事の目的のために、リストとマップが示されます。 詳細については、こちらの公式ドキュメントをご覧ください。
プロジェクトにImmutable.jsを追加するには、必ずターミナルにアクセスして次のコマンドを実行してください。
$ npm install immutable --save
また、使用しているデータ構造を、使用しているコンポーネントのImmutable.jsからインポートしてください。
import {Map, List} from 'immutable';
これは、Immutable.jsマップの使用方法です。
var foobar = {foo: "bar"}; var immutableFoobar = Map(foobar); console.log(immutableFooter.get("foo")); // => bar
また、配列を使用できます。
var helloWorld = ["Hello", "World!"]; var immutableHelloWorld = List(helloWorld); console.log(immutableHelloWorld.first()); // => Hello console.log(immutableHelloWorld.last()); // => World! helloWorld.push("Hello Mars!"); console.log(immutableHelloWorld.last()); // => Hello Mars!
Immutable.jsを使用することの欠点
Immutable.jsを使用することには、議論の余地のある主な欠点がいくつかあります。
お気づきかもしれませんが、APIを使用するのは少し面倒で、従来のJavaScript開発者はこれを好まないかもしれません。 より深刻な問題は、Immutable.jsがインターフェースをサポートしていないため、データモデルのインターフェースを実装できないことに関係しています。
要約
OnPush戦略がAngularのデフォルト戦略ではない理由を疑問に思われるかもしれません。 AngularがJavaScript開発者に不変オブジェクトの操作を強制したくなかったためだと思います。 しかし、それはあなたがそれを使用することを禁じられているという意味ではありません。
それが次のWebプロジェクトで活用したいものである場合は、Angularが別の変更検出戦略に簡単に切り替えることができることを理解できました。