NGRXセレクタ性能


NGRXセレクタは、memoizationを通してパフォーマンス利益を約束します.しかし、我々は我々のセレクタを定義するときに注意を払う必要がありますそれ以外の場合は、我々は記憶から恩恵を受けることが失敗する可能性があります!実際に我々は誤って我々のアプリケーションのパフォーマンスを低下させる可能性があります.

NGRXセレクタ


あなたがNGRXセレクタに慣れていないならば、またはブランドンRobertsからこの話をチェックしてくださいdocs . 彼らは基本的にあなたからのデータを抽出する方法ですStore .
次はどのように簡単にこのパフォーマンストラップに落ちることを見てみましょう!

カウンター申請


パフォーマンストラップを実証するために、我々はカウンタアプリを使用します.このコードで実験することができますStackBlitz この投稿を補完します.
つのカウンタとテキストボックスがあります.我々は、各カウンタの現在の値とすべてのカウンタの合計を表示します.

我々の州は以下のインターフェースを持っています.
export interface CounterState {
  counter1: number;
  counter2: number;
  name: string;
}

export interface BusyState {
  //lots of updates happen here!
}

export interface RootState {
  counter : CounterState;
  busyState: BusyState;
}
つの機能スライスがあることに注意してください.counter and busyState . busyState , 名前が示すように、多くの更新を受け取ります.

合計計算


我々の店で派生状態を格納したくないので、我々はその場で合計を計算する必要があります.私たちのテンプレートに表示される合計を計算するいくつかの方法があります.それぞれは、我々が現在検討する独自のパフォーマンス特性を持っています.

Adding two numbers is a trivial operation but for the sake of this post let's imagine it is a very expensive computation which we must minimise calculating.


コンポーネントの合計を計算する


我々は、注入されたストアとselect 演算子.
// Component
constructor(private store: Store<RootState>){}

this.total$ = store.pipe(select(state => 
                             state.counter.counter1 + state.counter.counter2)
                        );
しかし、このアプローチでは、計算は私たちの状態への変更ごとに再実行されます.それは、あらゆる変更を含みますBusyState これは全く関係なく、合計の値を変更することはありません!これは本当に我々のパフォーマンスに悪いので、我々は良いことができるかどうかを確認しましょう.

セレクターで還元器の合計を計算する


あなたが推測したように、我々はパフォーマンスを改善するためにセレクタを使うつもりです.我々は、ティムDeschryverによって説明されるように、これを使用して、これをします@ngrx/store . これらのCreator関数を使用すると、コンポーネントの合計計算を減らすことができます.
// Reducer
import { createSelector, createFeatureSelector } from "@ngrx/store";

const featureSelector = createFeatureSelector<CounterState>("counter");

export const getTotal = createSelector(
  featureSelector, s => s.counter1 + s.counter2
);
我々は我々の機能スライスとリターンを入力として取るcounter1 + counter2 私たちに合計の観測可能なストリームを与える.次に、合計を表示するには、このコンポーネントを使用します.
// Component
this.total$ = store.pipe(select(getTotal));
このセレクターを使用すると、合計計算はcounter 機能スライス.これは、もはや関連していない変更のために再実行されているとして大きな改善ですBusyState . しかし、我々はより良い行うことができます停止しないでください!

追記理解


この点で、我々がまだそれを完全に利用していないので、セレクターのメモ化がどのように働くかについて理解することは重要です.
に戻るdocs for selectors .

When using the createSelector and createFeatureSelector functions /store keeps track of the latest arguments in which your selector function was invoked. Because selectors are pure functions, the last result can be returned when the arguments match without re-invoking your selector function. This can provide performance benefits, particularly with selectors that perform expensive computation. This practice is known as memoization.


ここで重要な部分は@ngrx/store 最新の入力引数を追跡します.我々の場合では、これは全体ですcounter 機能スライス.
export const getTotal = createSelector(
  featureSelector, s => s.counter1 + s.counter2
);
なぜ我々がより良い行うことができますを参照してくださいcounter.name テキスト入力を介して.すべてのストロークでは、アクションはname . それは同じ機能スライスの一部であるため、各更新プログラムで我々の合計は再計算されています.

合成セレクタで計算する


私たちがドキュメントから学んだものを使用してgetTotal セレクタ自身の引数が変更されたときのみ実行されることを保証します.我々は、それを構成することによって、これをしますgetCounter1 セレクタとgetCounter2 セレクタ.これらのカウンタセレクタは、特定のカウンタ更新時に新しい値を出力します.これは順番に我々の議論を意味するgetTotal セレクタは、カウンタの値が変更されたときのみ変更されます.
// Reducer
export const getCounter1 = createSelector(
  featureSelector, s => s.counter1
);

export const getCounter2 = createSelector(
  featureSelector, s => s.counter2
);

// Composed selector
export const getTotal = createSelector(
  getCounter1, getCounter2, (c1, c2) => c1 + c2
);
この設定をcounter.name もはや再集計する合計を引き起こす!我々は最終的にmemoizationをフルに活用しているときに我々は絶対に計算する必要があります私たちは絶対に計算してください.これがセレクター構成の力だ.

実生活シナリオ


我々のデモアプリは、パフォーマンスの問題を持つにはあまりにも小さいが、これらの原則は大きなアプリケーションに大きな影響を適用することができます.
私が働いた1つのアプリでは、相互依存関係のドロップダウンの数を持っていた、すなわち1つの選択を更新すると、他のユーザーの利用可能なオプションをフィルタリングします.これは、すべてのルートストアをオフに動作するセレクタによって駆動されました.私は、これらのセレクタの低迷を調査することに頼まれました.私が最初にしたことは、各セレクターが走られる度にロギングを開始することでした.何百回も!
これは、あなたのセレクタを構成する重要性を発見したときです.上で概説したように、変化を作ることは、何百もからほんの一握りまでセレクタセレクタの数をもたらしました.パフォーマンス改善は劇的であり、セレクターはもはや低迷しなかった.

最後の思考


あなたがあなたのセレクターで計算的に高価な何かをしているならば、あなたは絶対にあなたがそうしなければならないとき、そのコードを実行することを確実としたいです.あなたのセレクタを構成する1つのテクニックは、これを達成することができますし、アプリケーションのパフォーマンスを保護します.