AngularでPipeを使うメリットとしては、「単純で、あらゆるコンポーネントでよく使うようなデータ整形処理は共通化して、Template(html)で記述できるようにする」というものです。
たとえば、%表記というのは内部的には 1.0 === 100% ですが、その変換は単純かつよく使うものです。
num1 = 2.5;
のときに
{{num1 | percent}}
と、PercentPipeを使って書くと、ビューの出力は
250%
になります。
Built-in Pipe として標準で用意されてるパイプには、下記のようによく使う単純なものが多いです。。
- DatePipe : 日付変換(yyyy/mm/dd形式等)
- DecimalPipe : 小数点切り捨て&ゼロうめ(3.141592 -> 003.14)
- JsonPipe : object->json化(JSON.stringifyと同じ)
- PercentPipe : パーセント表記(0.4 -> 40%)
- SlicePipe : 先頭n文字から、末尾m文字までを切り出す。
- CurrencyPipe : 通貨書式変換(USD->$,等)
- UpperCasePipe, LowerCasePipe, TitleCasePipe : 大文字小文字変換。(日本語だと必要性があまり理解できないが、英語圏では使用頻度高い)
PipeにするかMethodにするか
さて、似たようなことをするときに、カスタムPipeを作るか、Componentのメソッドにするか迷うときがあります。
また、PipeにはImpure(不純)パイプとPure(純粋)パイプがあり、Pureパイプが標準となっています。
この違いを簡単にテストしてみましょう。
https://plnkr.co/edit/cOhEpn9gAUBoKbFvDRtA?p=preview
- purePipe -> method -> impurePipe の順に無駄な実行が少ない。
- 変更検知(ChangeDetection)の関係で、methodは無駄に複数回呼ばれる。pipeは変更検知後にしか実行されないのでパフォーマンスが良い。
- impurePipeはホント無駄に呼ばれる。
- methodのほうがpurePipeより実行される回数が多くなっちゃう
- ただし描画処理前に実行されるから、描画に与える影響は超軽微
- 重い原因は大抵はDOM変更が重い。jsは軽い。
-
<div>{{ date.toString() }}</div>
は、date.toString()
の実行結果が変わらない限りDOM変更されない。 - ただ、purePipeだと date値で比較し変化があったときのみpipeメソッド実行するのに比べ、 method/impurePipeは
date.toString()
の実行結果で比較する感じ。
結論
■ pipeは共通処理・軽い処理・純粋関数のみにする
カスタムPipeにするかMethodにするか悩むときは、Pipeの元々の定義に従い
- (ビューでやるような)単純な変換か?
- 多くのコンポーネントで使用頻度が高くそのための共通化すべきか?
あたりを判断基準にするべきで、「描画速度向上の為にPipeにしよう!」ということは避けるべきでしょう。
■ 配列をソートやフィルタするpipeは作らない
コンポーネントのプロパティに sortedArray
や filteredArray
を作るほうがパフォーマンスが良い。
変更検知のタイミングとか実行処理が重いからとか色々ある。
詳しくは 公式ドキュメントにわざわざ項目作られてる ので参照してください(英語)。
■ メソッドは、ごく軽量なもの以外はtemplate内では使用しない
<h1>{{ getUserName(user.id) }} の BMI値 </h1>
<div>{{ user.calcBMI() }}</div>
こういうものは避ける。 getUserName()
メソッドが裏でSQLやHTTPリクエストを実行していた場合、変更検知(ChangeDetection) が実行されるたびにSQL/HTTPを発行してしまう。
user.calcBMI()
が内部的に user.weight / (user.height * user.height)
を呼び出してるだけの場合、テンプレート中で
<div>{{ user.weight / (user.height * user.height) }}</div>
とするのと処理の負荷は(ほぼ)変わらない。 (関数呼び出しのオーバーヘッドとか、スマホで3万回の試行で30ms程度の差)
ただし、可能ならコンポーネント初期化時に、下記のように初期化したほうが良い。
理由としては、テンプレートで複雑に処理を記述すると、見通しが圧倒的に悪くなるため。
user.bmi = user.weight / (user.height * user.height);
// templateで
<div>{{ user.bmi }}</div>
メソッド実行や計算は、テンプレート中からは除外したほうが無難。 動作するし、テスト中には便利なんですけどね。
ChangeDetectionが走るタイミングで、これらのメソッド実行や計算は、何度も何度も起きることになる。(値が変わってなくても!)