AngularでChangeDetectionによるパフォーマンスチューニング

angular

AngularでChangeDetectionによるパフォーマンスチューニングを行う方法です。

ChangeDetectionとは?

AngularではChangeDetectionと呼ばれる更新機構を持ってコンポーネントの情報が変更されるとページの再描画が行われます。

くわしい仕組みに関しては以下を参考にしてください。

Angular2のChange Detectionについて - Qiita

パフォーマンスチューニング

Angularのデフォルト設定では、全てのコンポーネントの変更に対して、このChangeDetectionによる更新が毎回走るようになっている。

例えば以下のようなコンポーネント構成時、つまりAコンポーネントの子コンポーネントとしてBコンポーネントとCコンポーネントが配置されているようなケースで、Bコンポーネントに対して変更が行われると本来更新不要なCコンポーネントに対する再描画処理も走ってしまいます。

componentパターン

ただ、AngularのVirtual DOM機構が上手に更新を行なってくれているらしく、本来ならここらへんは実装者は意識せずに構築ができる(はず)

ただ、大掛かりなHTMLレンダリングやメソッド等を利用した複雑な描画条件が指定されたコンポーネントが存在し、連続してChangeDetectionが呼ばれるようなケースではアプリケーションが重くなってしまうため対応しなくてはいけません。

つまり、上記の図ではCコンポーネントが描画コストが高い場合に、AコンポーネントやBコンポーネントなどCコンポーネントとは関係ない値が更新された際にはCコンポーネントの描画を行わないようにすることでアプリケーションが重くなってしまうのを防ぐことができます。

対応策は簡単で@Component()でコンポーネントを作成時に「changeDetection:ChangeDetectionStrategy.OnPush」の指定を行います。

@Component({
  selector: ...,
  template: ....,
  changeDetection:ChangeDetectionStrategy.OnPush
})

以下が動作サンプルです。

サンプル

このサンプルではappコンポーネント配下にchild1コンポーネント、child2コンポーネントを配置して、child2コンポーネントには「changeDetection:ChangeDetectionStrategy.OnPush」の指定を行い、それぞれのコンポーネントには自身を更新するためのカウントアップボタンを配置して更新時間を表示しています。

child2コンポーネントの更新時間は自身のボタンが押されないと更新されないのに対して、appコンポーネント、child1コンポーネントはどのボタンが押下されても更新されているのがわかります。

以下がソースコードです。

@Component({
  selector: 'child1',
  template: `
    <h1>child1:{{count}}</h1>
    <p>{{lastUpdated()}}</p>
    <input type='button'  value='countup' (click)='countup()'/>
  `,
})
export class Child1 {
  count:number = 0;
  countup():void{
    this.count++;
  }
  lastUpdated():String{
    return Date().toString();
  }
}
 
@Component({
  selector: 'child2',
  template: `
    <h1>child2:{{count}}</h1>
    <p>{{lastUpdated()}}</p>
    <input type='button'  value='countup' (click)='countup()'/>
  `,
  changeDetection:ChangeDetectionStrategy.OnPush
})
export class Child2 {
  count:number = 0;
  countup():void{
    this.count++;
  }
  lastUpdated():String{
    return Date().toString();
  }
}
 
@Component({
  selector: 'app',
  template: `<div>
    <h1>app:{{count}}</h1>
    <p>{{lastUpdated()}}</p>
    <input type='button'  value='countup' (click)='countup()'/>
    <child1></child1>
    <child2></child2>
    </div>`,
  changeDetection:ChangeDetectionStrategy.OnPush,
})
export class App {
  count:number = 0;
  countup():void{
    this.count++;
  }
  lastUpdated():String{
    return Date().toString();
  }
}

このように任意のタイミングでしか更新したくないコンポーネントに対してchangeDetection:ChangeDetectionStrategy.OnPushを指定することでパフォーマンスのチューニングを行うことができます。

構築するアプリケーションによりますが、複雑なアプリケーションなら最初からコンポーネントに対してchangeDetection:ChangeDetectionStrategy.OnPushを指定しておくのもよいでしょう。

続:AngularでChangeDetectionによるパフォーマンスチューニングに続きます。

関連エントリー

SystemJSでAngular 2の環境を構築する

スポンサードリンク

«[書評] Webディレクションの新・標準ルール | メイン | 続:AngularでChangeDetectionによるパフォーマンスチューニング»