Angular2のPipeを使う上で開発者が知るべきたった1つのこと

先日、学生向けにTodoワークショップを開催した時の話です。
Angular1のようにngForとPipeを組み合わせて、リストのソート機能をつくろうとしたところ、初め上手く動かせませんでした。
StackOverFlowで解決策を見つけてなんとか動かすことはできたのですが、腑に落ちず後日改めて調べたところ、Pipeを使う上で知っておくべき事を知らなかったことに気づきました。

今回はPipeを使う上で、これだけは最低限知っておくべき内容について紹介します。

tl;dr

  • 開発者が知るべきたった1つのことは、「Angular2のPipeにはpureimpureの2種類がある」ということ。
  • purePipeが想定通りのタイミングで動作しない時は、impureを使ってみるのも手。
  • ただし、Pipeでフィルタやソートを行うことはパフォーマンス上の懸念があるので、Componentにて行うことを推奨します。

英語が読める人は本家のドキュメントを読んでください。

pureimpureの違い

まず、Pipeにはpureimpureの2つあるのですが、デフォルトはpureです。2つの違いについて一言で表すとそれは「Pipeが評価されるタイミング(反応するchange detection)」の違いです。下にその違いについて示します。

  • pure
    • プリミティブ型(String, Number, Boolean, Symbol)の値が変わった時
    • オブジェクト型(Date, Array, Function, Object)の参照が変わった時
  • impure
    • 常に評価

ここで大事なのはpureでは、オブジェクトや配列内部の変更は無視してしまうということです。(このことを本家ではpure changeと表現しています。)
これは常にDeep checkをすることを避け、パフォーマンスを向上させるために必要なことですが、うっかりこの事を知らないとPipeが想定通りの動きをしない事態が容易に起こりえます。

impurePipeの作り方

impurePipeを作るためには、@Pipe宣言の部分でプロパティにpure: falseを指定することでできます。

1
2
3
4
5
@Pipe({
name: 'impurePipe',
pure: false
})
export class MyImpurePipe {}

非常に簡単なのですが、このとき「pureって何?」となるわけです。

まとめ

ではimpurePipeをガンガン使っていいのでしょうか?

本家のドキュメントに次の一文がありました。

No FilterPipe or OrderByPipe

Pipeでフィルタやソートは行わないでくれと行っています。本家ではこれらの機能をComponent側に移動することを推奨しています。
理由の1つとしてフィルタやソートをPipeで行うために、必ず処理対象のオブジェクトの参照が必要となり、非常にコストが高い処理となり易いことが挙げられます。

Angular1の時代からng-repeatとFilterを組み合わせてた場合、うっかり n×nの計算量となってしまい、パフォーマンス上の問題を出すことが多々ありました。
Angular2でも同様に、ngForimpurePipeをうっかり組み合わせることは避けた方がいいと言えます。

おまけ