まずはこのステップを実行してみて - 2025年のSEOとUXを一気に底上げする即効テクまとめ
- PageSpeed Insightsで全ページのLCP・CLSを7日以内に測定
数値で遅い箇所特定、SEO順位低下や直帰リスク減
- スマホ・PC両方でレスポンシブ動作を必ず確認
60%以上がモバイル流入、ユーザー離脱抑制&Google評価UP
- *ngFor利用時は常にtrackBy関数追加、要素数10件以上なら必須
*ngFor未指定だとDOM再生成増加→速度劣化
- `lodash`など大型ライブラリはimport単位で厳選し全体容量10%以下目安へ
`import地獄`防止&初回表示高速化、一括読み込み禁止
2025年のWeb速度、なぜ大事?UXとSEOの関係が微妙に交差
### 2025年におけるAngularアプリケーションのパフォーマンス向上ベストプラクティス
2025年、相変わらずAngularはフロントエンド界隈で存在感があるよなあ、とか思いつつ。でも実際のところ、その多機能っぷりが裏目に出て、パフォーマンスを意識しないとすぐ重くなる。えーっと、特にエンタープライズ向けアプリとか、PWA(Progressive Web Apps)みたいな軽量志向の開発ではなおさらだ。最近はどんな用途でも「速い」「サクサク動く」が当たり前になってきてる気がするし。
なんか最適化されてないAngularアプリだと、ページ読み込み遅いとか、UIの反応がワンテンポ遅れるとか…地味だけど地獄。ま、それでユーザー離れちゃうことも普通にあるみたいね。うーん、自分もたまにイラッとするから他人事じゃない。ただAngular自体にはハイパフォーマンスなコードを書くためのツールや技法はいろいろ揃ってる。それ全部使いこなせれば―まあ現実そんな簡単じゃないけど―かなり快適になる可能性もあるんだろう。今ここで語るべきは**2025年版 Angular パフォーマンス向上ベストプラクティス**ってことでいいのかな?途中ちょっと関係ない話しそうになったけど戻すね。
具体的には遅延ロード(lazy loading)、チェンジディテクション戦略、それからメモ化とかバンドルサイズ最適化について触れていく予定。その上で実装時迷わないように**コード例**も載せておくつもり。
### 2025年におけるパフォーマンス重視の理由
2025年にもなるとさ、多くのユーザーが「もっと早く!もっとレスポンス良く!」みたいな要求を突きつけてくる感じ?いや昔からそうだったかもしれんけど…最近はその傾向さらに強まっている気配が濃厚だと思う。本当に一瞬待たされただけでも、「もういいや」ってタブ閉じられる危険性あるしさ。ま、いいか。しかし現場ではそれこそ数ミリ秒単位で工夫している開発者も増えていて、自分もちょっと焦るというか…。ともあれ、この流れは当面続くだろうね。
2025年、相変わらずAngularはフロントエンド界隈で存在感があるよなあ、とか思いつつ。でも実際のところ、その多機能っぷりが裏目に出て、パフォーマンスを意識しないとすぐ重くなる。えーっと、特にエンタープライズ向けアプリとか、PWA(Progressive Web Apps)みたいな軽量志向の開発ではなおさらだ。最近はどんな用途でも「速い」「サクサク動く」が当たり前になってきてる気がするし。
なんか最適化されてないAngularアプリだと、ページ読み込み遅いとか、UIの反応がワンテンポ遅れるとか…地味だけど地獄。ま、それでユーザー離れちゃうことも普通にあるみたいね。うーん、自分もたまにイラッとするから他人事じゃない。ただAngular自体にはハイパフォーマンスなコードを書くためのツールや技法はいろいろ揃ってる。それ全部使いこなせれば―まあ現実そんな簡単じゃないけど―かなり快適になる可能性もあるんだろう。今ここで語るべきは**2025年版 Angular パフォーマンス向上ベストプラクティス**ってことでいいのかな?途中ちょっと関係ない話しそうになったけど戻すね。
具体的には遅延ロード(lazy loading)、チェンジディテクション戦略、それからメモ化とかバンドルサイズ最適化について触れていく予定。その上で実装時迷わないように**コード例**も載せておくつもり。
### 2025年におけるパフォーマンス重視の理由
2025年にもなるとさ、多くのユーザーが「もっと早く!もっとレスポンス良く!」みたいな要求を突きつけてくる感じ?いや昔からそうだったかもしれんけど…最近はその傾向さらに強まっている気配が濃厚だと思う。本当に一瞬待たされただけでも、「もういいや」ってタブ閉じられる危険性あるしさ。ま、いいか。しかし現場ではそれこそ数ミリ秒単位で工夫している開発者も増えていて、自分もちょっと焦るというか…。ともあれ、この流れは当面続くだろうね。
ビルド前にやるAOT設定、小さなjsonで大きな違い?
パフォーマンスって、うーん、ただ速ければいいってものでもないんだよね。ユーザー体験がどうとか、SEOランキングにも絡むし…まあ収益の話になるとちょっとリアリティ増す気もする。Google Core Web Vitalsだけど、いまや検索表示の順位に直結する要素になってるらしいんだよな。そうそう、最近はモバイルユーザーが圧倒的に多いし――あっ、この前駅のWi-Fi遅くてイラッとしたけど――ネット環境が全部快適とは限らないから最適化が本当に大事なんだと思う。ある調査でね、たった100ms遅れるだけでもコンバージョン率下がるかもしれないって聞いたことある。でも、それ本当なのかな。いや多分本当なんだろうな…。さて、ここではAngularアプリケーションを効率的で拡張性も保ちながら、高速化する方法について考えてみたいと思う。えっと…話戻すと、そのへん全部ひっくるめて工夫していかないとダメなんだよね。
### 1. Ahead-of-Time(AOT)コンパイルを有効にする
AngularのAhead-of-Time(AOT)コンパイルというものがあるんだけど…これはHTMLやTypeScriptをビルド時にJavaScriptへ変換しておいて、実行時じゃなくて事前処理できる仕組みなんだ。それによって初回表示速度も上げやすくなるし、何より無駄な計算減らせる感じ?ああ…余談だけど昔はJITしか使わなかった気がするけど今はAOT推奨みたいでさ。でもまあ詳しくは後述するとして、とりあえずAOTを有効にしておけば基本的には恩恵受けやすいと思う。ま、いいか。また話逸れたな…。
### 1. Ahead-of-Time(AOT)コンパイルを有効にする
AngularのAhead-of-Time(AOT)コンパイルというものがあるんだけど…これはHTMLやTypeScriptをビルド時にJavaScriptへ変換しておいて、実行時じゃなくて事前処理できる仕組みなんだ。それによって初回表示速度も上げやすくなるし、何より無駄な計算減らせる感じ?ああ…余談だけど昔はJITしか使わなかった気がするけど今はAOT推奨みたいでさ。でもまあ詳しくは後述するとして、とりあえずAOTを有効にしておけば基本的には恩恵受けやすいと思う。ま、いいか。また話逸れたな…。

モジュールは全部先読みしない、新しいLazy Loading術
### 何をするべきか: Angular CLIの設定でAOTがデフォルトで有効になっていることを確認します。
えっと、ああ…ちょっと待って、なんか頭がぼーっとしてきたけど、angular.jsonを開いて「configurations」の中身を覗くと、「production」プロファイルに「aot: true」が入ってるの、ちゃんと見ておいた方がいいんだよね。
// angular.json
"configurations": {
"production": {
"aot": true,
...
}
}
AOT(Ahead-Of-Time)コンパイルは、実行時にAngularコンパイラ自体を必要としないから、その分だけバンドルサイズが小さくなることもあるし、パフォーマンスも微妙に良くなる…らしい。でも本当に全部そうなのかな。まあ、一応公式でも推されてるので、不安だったらもう一回確認したほうが安心かもしれない。ま、とりあえず。
### 2. モジュールの遅延読み込み(Lazy Loading)の利用
ああ、そういえばこれ前にも似たような話したっけ…。遅延読み込みっていうやつは、本当に必要なタイミングまでモジュールを読み込まずに済む方法なんだけど、それによって初回ロード時のバンドルサイズが縮む場合もあるとかないとか…。いや、多分縮むんだろうけど、自分でも試してみないとなぁ。
**遅延読み込みなしの場合:**
@NgModule({
imports: [DashboardModule, ProfileModule], // 全てのモジュールが最初に読み込まれる
})
……今ちょっと脱線したけど(コーヒー飲みたい)、本筋戻すと—
Angular 17以降なら、スタンドアロンコンポーネントも遅延読み込みできるようになったみたいで:
### 3. `OnPush`によるチェンジディテクションの最適化
さて、「ChangeDetectionStrategy.Default」だとコンポーネントツリー全体をごそっとチェックしちゃうから、大規模な画面構成になると結構重くなることが…実際あるんじゃないかな?僕もちょっと疑問残る時あるけど。「OnPush」戦略を選ぶことで、本当に必要な変更だけ検知できたりするので、パフォーマンス意識するなら部分的に使いたい場面は多いはず。ま、急ぎじゃなければ後回しでもいいかもしれないけど、本気で速さ求めてるなら検討価値ありだね。
えっと、ああ…ちょっと待って、なんか頭がぼーっとしてきたけど、angular.jsonを開いて「configurations」の中身を覗くと、「production」プロファイルに「aot: true」が入ってるの、ちゃんと見ておいた方がいいんだよね。
// angular.json
"configurations": {
"production": {
"aot": true,
...
}
}
AOT(Ahead-Of-Time)コンパイルは、実行時にAngularコンパイラ自体を必要としないから、その分だけバンドルサイズが小さくなることもあるし、パフォーマンスも微妙に良くなる…らしい。でも本当に全部そうなのかな。まあ、一応公式でも推されてるので、不安だったらもう一回確認したほうが安心かもしれない。ま、とりあえず。
### 2. モジュールの遅延読み込み(Lazy Loading)の利用
ああ、そういえばこれ前にも似たような話したっけ…。遅延読み込みっていうやつは、本当に必要なタイミングまでモジュールを読み込まずに済む方法なんだけど、それによって初回ロード時のバンドルサイズが縮む場合もあるとかないとか…。いや、多分縮むんだろうけど、自分でも試してみないとなぁ。
**遅延読み込みなしの場合:**
@NgModule({
imports: [DashboardModule, ProfileModule], // 全てのモジュールが最初に読み込まれる
})
……今ちょっと脱線したけど(コーヒー飲みたい)、本筋戻すと—
**遅延読み込みを用いる場合:**
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () =>
import('./dashboard/dashboard.module').then((m) => m.DashboardModule),
},
];
Angular 17以降なら、スタンドアロンコンポーネントも遅延読み込みできるようになったみたいで:
{
path: 'about',
loadComponent: () =>
import('./about/about.component').then((m) => m.AboutComponent),
}
うーん、新しい書き方に慣れなくて手こずる人もいると思う。でも、この機能使わないともったいない気もするし…。
### 3. `OnPush`によるチェンジディテクションの最適化
さて、「ChangeDetectionStrategy.Default」だとコンポーネントツリー全体をごそっとチェックしちゃうから、大規模な画面構成になると結構重くなることが…実際あるんじゃないかな?僕もちょっと疑問残る時あるけど。「OnPush」戦略を選ぶことで、本当に必要な変更だけ検知できたりするので、パフォーマンス意識するなら部分的に使いたい場面は多いはず。ま、急ぎじゃなければ後回しでもいいかもしれないけど、本気で速さ求めてるなら検討価値ありだね。
ChangeDetection戦略OnPushか、そのままか。入力・イベントの落とし穴も一緒に
【デフォルト戦略】
typescript
@Component({
selector: 'heavy-component',
templateUrl: './heavy.component.html',
})
export class HeavyComponent {}
うーん、まあ普通にコンポーネントを書くだけだと、こういう感じになるよね。でも、本当にこれで良いのか?いや、ちょっと待てよ…やっぱりパフォーマンスを考えなきゃと思ったりもする。</code></pre>
【最適化された戦略】
typescript
@Component({
selector: 'heavy-component',
templateUrl: './heavy.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HeavyComponent {}
ああ、ここに `ChangeDetectionStrategy.OnPush` を追加するだけで挙動がガラッと変わる。要するに(…あ、ごめん口癖)、Angularがこのコンポーネントをチェックするタイミングってさ——@Input() が変更された時とか、内部イベント発生時だけに限定される。ま、いいか。それでも分かりづらいなと自分でも思うけど、とにかく頻繁な検証から解放される感じはある。
でね、一応補足しておくと、イミュータブルなデータ構造を使うのが推奨なんだって。ただ、自分もちょっと前まで気にしてなかったから偉そうには言えないんだけどさ。
### 4. *ngForでTrackByを利用する
*ngForって便利なんだけど、そのままだとリスト全体が再描画されるんだよね…。あっ、忘れてたけど下記のようになる。
【trackByなしの場合】
xml
<li item of items>{{ item.name }}</li>
へぇー、この書き方見慣れているはずなのに、実際大規模リストだと無駄が多いのかな。自分なら面倒くさいって放置しちゃいそうだけど。
で、本筋戻すけど——
【trackByありの場合】
xml
<li item of items trackby: trackbyid>{{ item.name }}</li>
typescript
trackById(index: number, item: any): number {
return item.id;
}
ふぅ…。これならDOMの再生成量が特に大規模リストではぐっと減ることになってるらしい。でも本当にそこまで効果あるのかな?疑いつつも現場では結構重宝されているみたい。
### 5. asyncパイプを使い手動サブスクリプションは避ける
Angularの`async`パイプね。正直、自分は昔よく手動でsubscribeしちゃって後悔したクチです。まあ、それより…
【望ましい実装例】
xml
<h2>{{ user$ | async }}</h2>
一見地味なんだけど、この書き方ひとつでObservablesへの購読・解除が自動化される。不思議だよね。でもちょっと安心感あるというか…。
さて話逸れたので戻すけど、
【好ましくないパターン】
typescript
ngOnInit() {
this.user$.subscribe(user => this.user = user);
}
このコード見るたび「またメモリリーク?」って顔しかめてしまう癖がついた(昔いっぱいバグ出した)。asyncパイプは `ChangeDetectionStrategy.OnPush` と合わせて使うと性能向上も期待できたりするんだけど――まあ注意点もあるから油断せず確認したほうがいい。

*ngForでtrackBy忘れたら…リスト巨大化が招くDOM地獄
### 6. 未使用の依存関係をツリーシェイキングで除去する
アプリケーションが**ESモジュール**を使っている場合、未使用のライブラリとか関数がバンドルに混ざらないようにしたいんだよね。なんか昔、全部まとめてimportしてた時代もあった気がするけど…今はもう、そんなやり方は非推奨というか。うーん、必要なものだけインポートするのが基本になった。
import { debounceTime } from 'rxjs/operators';
実際、こういう書き方ならOK。でも…何となく「あれもこれも」と欲張って、
import * as _ from 'lodash'; // サイズが大きくなるし無駄多い
みたいにすると後悔する羽目になる。ま、いいかと思っててもビルドサイズ増えるからさ。そうじゃなくて——いや途中で話逸れるけど最近パフォーマンス気にしてる人増えたよね——結局こういう感じで書けばいいわけ。
この方法だとツリーシェイキングの恩恵を最大限受けられる…たぶん。それでも全部自動で消えるとは限らないけど、一応ベターな選択肢なんじゃないかな、と自分では思う。
アプリケーションが**ESモジュール**を使っている場合、未使用のライブラリとか関数がバンドルに混ざらないようにしたいんだよね。なんか昔、全部まとめてimportしてた時代もあった気がするけど…今はもう、そんなやり方は非推奨というか。うーん、必要なものだけインポートするのが基本になった。
import { debounceTime } from 'rxjs/operators';
実際、こういう書き方ならOK。でも…何となく「あれもこれも」と欲張って、
import * as _ from 'lodash'; // サイズが大きくなるし無駄多い
みたいにすると後悔する羽目になる。ま、いいかと思っててもビルドサイズ増えるからさ。そうじゃなくて——いや途中で話逸れるけど最近パフォーマンス気にしてる人増えたよね——結局こういう感じで書けばいいわけ。
import debounce from 'lodash/debounce';
この方法だとツリーシェイキングの恩恵を最大限受けられる…たぶん。それでも全部自動で消えるとは限らないけど、一応ベターな選択肢なんじゃないかな、と自分では思う。
asyncパイプと手動購読―どちらがメモリを救う?観察者の選択肢
HTTPキャッシュとかページネーションの話になると、なんだか頭がぼんやりしてくるけど…まあ、今さら逃げられないよね。無駄に大規模なデータセットを全部取ってくるのは絶対おすすめできないし、ああ、本当にこれは避けた方が身のため。でもさ、RxJSオペレーターを使うほうがいいって言われても、「じゃあどうする?」って一瞬考えちゃう。例えばこういうコードがある——
getItems(): Observable
getItems(): Observable
- {
return this.http.get- (url).pipe(
shareReplay(1), // レスポンスをキャッシュ
catchError(this.handleError)
);
}
——こんな感じでレスポンスのキャッシュも効いている(shareReplayだし)。でも、ちょっと思い出したけど、大きいリスト扱う場合はやっぱりバックエンド側でページネーション実装しないと後悔することになりそう。いや、たぶん本当にそう。
……で、それだけじゃ終わらなくて。サードパーティ製パッケージを追加しまくるとバンドルサイズがキロバイト単位どころかメガバイト単位まで増えてしまうという恐ろしい現象、知らずにやっちゃった人も多いよね(自分も以前…)。まあ軽量な代替策選びましょうってなるけど、それ簡単そうで実際はかなり面倒だったりする。「重いチャートライブラリ」とか、
<pre><code class="language-html">import * as Highcharts from 'highcharts';
みたいなの入れる前に、一呼吸置いて考えるべきかもしれない。Chartとかもっと軽めのもの試せば良かったなあと反省してたりして…。ほんと集中力持たない日だ。でも、とにかく注意深く選んでおけばあとから困らないと思う、多分ね。

import地獄避けてTree Shakingへ、lodash丸ごとは絶対NG?
js や Angular 独自の `
HTTP呼び出しキャッシュかページ分割か…大量データとの付き合い方再考察
Angular CLIはデフォルトでアセットを自動的に圧縮したり、ミニファイしてくれるんだよね。JavaScriptのミニファイもそうだし、コメントも勝手に削除される(これちょっと寂しい気もする…まあ、仕方ない)。それからgzip圧縮もサポートしてるけど、これってウェブサーバー側で有効化しないと実際には効かないから注意したほうがいいかもしれない。ああ、今思い出したけど、「本番環境」ってやつでは、gzipとかbrotli圧縮を利用するのが推奨されているらしい。でも…キャッシュについて深く考え始めたらキリがないな。静的アセットのキャッシュも本番サーバーでは大事みたい。ま、結局みんな速度命なんだろうな。
ユーザー入力ね…例えば検索フィールドとかでAPIコール飛ばす場合、そのたびに毎回リクエスト投げるのは非効率っていうか正直ウザいと思うわけで…。ここで`debounceTime`を使ったほうが良い、と言われている。えっと…コードを書くとこうなる:
this.searchControl.valueChanges
.pipe(debounceTime(300))
.subscribe((query) => this.search(query));
なんか無機質だけど、それが現実なんだろうなあ。でもさ、このdebounceって単語自体ちょっと耳慣れなくて最初戸惑った覚えあるんだよね。まあ、それは置いておいて。
あとさ、CPU負荷高めな処理――計算量多いやつとか――そういうときはWeb Workerを活用することになってるっぽい。でも正直、普通の開発者が日常的にWeb Worker書くことって多くはないような?いやでも、本当に重い処理なら必須になる場面もあるのか…。ふと別件思い出しかけたけど、この話に戻すと、とにかく負荷対策として考えておいたほうが後々泣きを見ずに済むということかな。
ユーザー入力ね…例えば検索フィールドとかでAPIコール飛ばす場合、そのたびに毎回リクエスト投げるのは非効率っていうか正直ウザいと思うわけで…。ここで`debounceTime`を使ったほうが良い、と言われている。えっと…コードを書くとこうなる:
this.searchControl.valueChanges
.pipe(debounceTime(300))
.subscribe((query) => this.search(query));
なんか無機質だけど、それが現実なんだろうなあ。でもさ、このdebounceって単語自体ちょっと耳慣れなくて最初戸惑った覚えあるんだよね。まあ、それは置いておいて。
あとさ、CPU負荷高めな処理――計算量多いやつとか――そういうときはWeb Workerを活用することになってるっぽい。でも正直、普通の開発者が日常的にWeb Worker書くことって多くはないような?いやでも、本当に重い処理なら必須になる場面もあるのか…。ふと別件思い出しかけたけど、この話に戻すと、とにかく負荷対策として考えておいたほうが後々泣きを見ずに済むということかな。

サードパーティ減量化計画:HighchartsやChart.jsの重み、代替案あり?
g. 、画像処理の場合とか、Web Workerに処理をオフロードすることができるんだよね。
ng generate web-worker imageProcessor
ああ、コマンドはこう書くんだけど…正直、最初ちょっと戸惑った。でもまあ、これでUIの応答性を保てるってわけか。ま、いいか。
### 14. スタンドアロンコンポーネントを利用する
Angular 14以降で導入された**スタンドアロンコンポーネント**はね、なんというかボイラープレートコードが減らせるし、その分アプリケーションのブートストラップも速くなる「かもしれない」。いや、本当に効果あるのかな、と自分でも思うときある。えっと…
なんだろう、この記述って一見シンプルそうだけど…やっぱり慣れないと混乱する人もいそう。まぁ、それはさておき本筋へ戻ろう。
### 15. ライフサイクルフックの最適化
`ngOnInit`や`ngAfterViewInit`などのライフサイクルフックにはさ、不必要なロジックを書いちゃダメってよく言われるんだよなぁ。実際、自分もついつい色々詰め込んじゃいたくなるけど…。ま、それが推奨されているから注意した方が良いみたい。でも時々、「本当に全部整理できるものなの?」とか疑問に思ったりして。うーん、それでも軽く意識しておいたほうが吉っぽいよね。
ng generate web-worker imageProcessor
ああ、コマンドはこう書くんだけど…正直、最初ちょっと戸惑った。でもまあ、これでUIの応答性を保てるってわけか。ま、いいか。
### 14. スタンドアロンコンポーネントを利用する
Angular 14以降で導入された**スタンドアロンコンポーネント**はね、なんというかボイラープレートコードが減らせるし、その分アプリケーションのブートストラップも速くなる「かもしれない」。いや、本当に効果あるのかな、と自分でも思うときある。えっと…
**例:**typescript
@Component({
standalone: true,
selector: 'app-header',
imports: [CommonModule],
template: `<h1>Welcome</h1>`,
})
export class HeaderComponent {}
なんだろう、この記述って一見シンプルそうだけど…やっぱり慣れないと混乱する人もいそう。まぁ、それはさておき本筋へ戻ろう。
### 15. ライフサイクルフックの最適化
`ngOnInit`や`ngAfterViewInit`などのライフサイクルフックにはさ、不必要なロジックを書いちゃダメってよく言われるんだよなぁ。実際、自分もついつい色々詰め込んじゃいたくなるけど…。ま、それが推奨されているから注意した方が良いみたい。でも時々、「本当に全部整理できるものなの?」とか疑問に思ったりして。うーん、それでも軽く意識しておいたほうが吉っぽいよね。
バンドル解析から圧縮・Preloadまで―最終仕上げ&細かな小技集
ngOnInit() {
// うーん、ここはただのセットアップだけど、実際これで十分な時も多い。いや、本当はもっと書きたいことあった気がするけど…ま、いいか。シンプルにしておくと頭も混乱しないしね。
}
ngAfterViewInit() {
// ああ、これはやっちゃダメな例。つい高負荷なDOMクエリを書きたくなる日もあるけど、それ普通によくないよね。でもなんか昔やってた記憶が蘇る…うっかり癖にならないよう注意したい、と自分に言い聞かせて本題へ戻る。
}}
trackById(index: number, user: User) {
return user.id;
}
constructor(private userService: UserService) {
// うーん、ここはただのセットアップだけど、実際これで十分な時も多い。いや、本当はもっと書きたいことあった気がするけど…ま、いいか。シンプルにしておくと頭も混乱しないしね。
}
ngAfterViewInit() {
// ああ、これはやっちゃダメな例。つい高負荷なDOMクエリを書きたくなる日もあるけど、それ普通によくないよね。でもなんか昔やってた記憶が蘇る…うっかり癖にならないよう注意したい、と自分に言い聞かせて本題へ戻る。
}}
typescript
@Component({
selector: 'app-user-list',
template: `
<ul>
<li user of users async trackby: trackbyid>
{{ user.name }}
</li>
</ul>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserListComponent {
users$ = this.userService.getUsers();
<pre><code>
trackById(index: number, user: User) {
return user.id;
}
constructor(private userService: UserService) {