こんにちは!
Angularアプリケーションのパフォーマンスを最適化しようとすると、必ず突き当たるのが「Change Detection(変更検知)」というキーワードです。しかし、その仕組みは少し複雑で、初心者の方には難しく感じられるかもしれません。
DefaultとOnPushって何が違うの?
このような悩みを解決するためには、Angularがどのようにデータの変更を検知し、画面を更新しているかを知る必要があります。
この記事では、Angularの Change Detection(変更検知)の基本的な仕組みから、パフォーマンスを劇的に向上させるOnPush戦略、そして手動で更新をトリガーする方法まで を、図解するようなイメージで分かりやすく解説します。
この記事は次のような方におすすめです。
- Angularの動作原理を深く理解したい方
- アプリケーションの動作が重くて困っている方
ChangeDetectionStrategy.OnPushの意味と効果を知りたい方- 意図通りに画面更新ができずに悩んでいる方
- 大規模なAngularアプリの開発に携わっている方
この記事を読めば、Angularの変更検知メカニズムがクリアになり、アプリケーションのパフォーマンスを自在にコントロールできるようになります。無駄な処理を減らし、サクサク動く快適なアプリケーションを作りましょう。
「Angularの裏側の仕組みをマスターしたい方」や「パフォーマンスチューニングに興味がある方」は、ぜひ参考にしてください。
それでは、順を追って詳しく見ていきましょう!
Change Detection(変更検知)とは何か
AngularにおけるChange Detection(変更検知)とは、 コンポーネントのデータ(モデル)が変更されたときに、それを自動的に画面(ビュー)に反映させる仕組み のことです。
私たちが普段、変数の値を書き換えるだけで画面がパッと切り替わるのは、Angularが裏側で「値が変わったかどうか」を常に監視してくれているおかげです。
どのようなタイミングで動くのか
Angularの変更検知は、主に以下のような非同期イベントが発生した後に実行されます。
- ユーザーの操作: クリック(click)、入力(input)、送信(submit)など
- HTTP通信: サーバーからのレスポンスを受け取ったとき(XHR/Fetch)
- タイマー処理:
setTimeoutやsetIntervalなどの時間が経過したとき
Angularは「Zone.js」というライブラリを使ってこれらのイベントを監視しており、何かが起きるたびに「データが変わったかもしれないからチェックしよう!」と動き出します。
デフォルトの検知戦略:Default
Angularでコンポーネントを作成すると、デフォルトでは ChangeDetectionStrategy.Default という戦略が適用されます。
特徴:基本的にはツリー全体をチェックする
Default戦略の最大の特徴は、 何かイベントが起きるたびに、コンポーネントツリーを上から順にチェックしにいく という点です。
親コンポーネントでクリックイベントが発生した場合、Angularはルート(最上位)から順に変更がないか確認していきます。ただし、後述する OnPush戦略が設定されているコンポーネントに到達し、かつ入力値に変更がない場合は、その先の子孫コンポーネントのチェックをまるごとスキップします。
すべてがDefault戦略のままだと、関係のない末端のコンポーネントまで毎回確認が走ることになり、 コンポーネントの数が増えてくると、無駄なチェックが大量に発生し、パフォーマンス低下の原因になることがあります。
パフォーマンスを上げる:OnPush戦略
そこで登場するのが、より効率的な ChangeDetectionStrategy.OnPush 戦略です。これを適用すると、Angularに対して「このコンポーネントは、特定の条件のときだけチェックしてくれればいいよ」と指示を出すことができます。
OnPushの設定方法
使い方は簡単で、コンポーネントのメタデータに設定を追加するだけです。
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.component.html',
// ここでOnPushを指定
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserProfileComponent {
// ...
}
チェックが走る条件
OnPushを設定したコンポーネント(とその子孫)は、基本的に変更検知の対象から外れます(スキップされます)。チェックが走るのは、以下の限られたタイミングだけになります。
- @Input()の値(参照)が変更されたとき
- そのコンポーネント自身や子コンポーネントでイベントが発生したとき
- 非同期パイプ(AsyncPipe)から新しい値が流れてきたとき
- 手動で変更検知をリクエストしたとき
特に重要なのが1つ目の条件です。Angularはオブジェクトの中身までは見に行かず、 「メモリ上の参照先が変わったかどうか」だけをチェックします。 つまり、オブジェクトのプロパティを書き換えただけでは検知されず、新しいオブジェクトごと代入し直す(イミュータブルな更新をする)必要があります。
これにより、無駄なチェックを大幅にスキップできるため、アプリケーション全体のパフォーマンスが劇的に向上します。
手動で更新を制御する
OnPushを使っていると、外部のイベントや非同期処理の結果など、Angularが自動で検知してくれないタイミングでデータを更新したい場合があります。そんなときは、ChangeDetectorRefを使って手動でAngularに合図を送る必要があります。
markForCheck()
最もよく使われるのが markForCheck() です。
import { Component, ChangeDetectorRef, OnInit } from '@angular/core';
@Component({
// ... OnPush設定済み
})
export class MyComponent implements OnInit {
data: string;
constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() {
// Angular管理外の処理などでデータが変わった場合
someExternalService.subscribe(newData => {
this.data = newData;
// 「次の検知サイクルでこのコンポーネントをチェックしてね」とマークする
this.cdr.markForCheck();
});
}
}
markForCheck() を呼ぶと、そのコンポーネントからルート(根っこ)までのパスに「チェックが必要」という目印がつきます。そして、次の変更検知サイクルが回ってきたときに、マークされた部分だけが更新されます。
detectChanges()
もう一つは detectChanges() です。こちらは「次のサイクル」を待つのではなく、 「今すぐ」そのコンポーネントと子孫の変更検知を強制的に実行します。
updateNow() {
this.data = 'すぐに反映したいデータ';
// 今すぐチェックを実行!
this.cdr.detectChanges();
}
通常は markForCheck() で十分ですが、Angularのイベントループから外れた処理や、極めて特殊なタイミングで即座に画面更新が必要な場合に使われます。
ひとつひとつ真摯に向き合う企業
株式会社 ONE WEDGEでは、新たな仲間を募集しています!
私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。
- 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
- 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇、有給6日取得で特別休暇付与)を完備!
- 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
- 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
- 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
- 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
- ワークライフバランスを重視し、副業もOK!
株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!
ホームページ、採用情報は下記ボタンからご確認ください!
応募、ご質問など、お問い合わせフォーム、またはX (旧Twitter)、InstagramのDMでお気軽にご相談ください♪
まとめ
今回は、AngularのChange Detection(変更検知)の仕組みと、DefaultとOnPushの違いについて解説しました。
- Angularはイベント発生時にコンポーネントツリーを巡回して変更を検知します。
Default戦略は基本的にはツリー全体をチェックするため、手軽ですが大規模になると無駄な処理が増える可能性があります。OnPush戦略はチェックの回数を最小限に抑え、パフォーマンスを向上させます。OnPushでは、Inputの参照変更やイベント発生時のみ更新されます。- 必要に応じて
markForCheck()などを使い、手動で更新を制御することが重要です。
小規模なアプリならDefaultのままでも問題ありませんが、ある程度規模が大きくなってきたら、ぜひOnPushの導入を検討してみてください。仕組みを理解して使いこなせば、Angularアプリはもっと高速になります。
※本記事の本文案はAIを活用して作成していますが、記載しているコードは筆者が実際に実行・検証し、内容の正確性を確認したうえで公開しています。


