10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

この記事がAngular #2 Advent Calendar 2019
23日目の記事です。

Event Bubbling の性能の問題

下記のHTMLコードを見ましょう。

<div (click)="doSomething()">
  <button (click)="doAnotherThing()">Button</button>
</div>

Event Bubbling のため、DivのなかのButtonをクリックしたら、両方のEvent ListenerがTriggerされます。この2つのEvent Listenerが全部Change Detectionを実行します。実際がこのようなケースでChange Detectionを一回だけ実行したいです。特にこのようなケースがAngular MaterialとかUI ライブラリで結構普通のケースですので、性能改善したいです。

実行順番

まず上記のケースでEvent ListenerとChange Detectionの実行順番を見ましょう。

export class AppComponent {
  doSomething() { // event listener for parent div
    console.log('event listener for parent div');
  }
  doAnotherThing() { // event listener for inner button
    console.log('event listener for inner button');
  }
}

そして、Zone.jsがEvent ListenerをMonkey patchされましたので、下記のような感じのコードになりました。

function eventHandler(...) {
  try {
    realHandler(...); // doSomething あるいはdoAnotherThingのDelegate
  } finally {
    applicationRef.tick();
  }
}

そしたら、Buttonをクリックするとき、実行の順番が下記のようになりました。

event listener for inner button
applicationRef.tick
event listener for parent div
applicationRef.tick

実際ほしいのは

event listener for inner button
event listener for parent div
applicationRef.tick

です。

解決方法

Change Detectionを同期ではなく、非同期で実行することです。

isChangeDetectionScheduled = false;
function eventHandler(...) {
  try {
    realHandler(...); // doSomething あるいはdoAnotherThingのDelegate
  } finally {
    if (isChangeDetectionScheduled) {
      return;
    }
    isChangeDetectionScheduled = true;
    requestAnimationFrame(() => {
      isChangeDetectionScheduled = false;
      applicationRef.tick();
    }); 
  }
}

のような感じのコードでChange DetectionをrequestAnimationFrameのSchedulerで実行させ、そしてもしScheduleされたTaskがあったら、スキップして、なかったら、Scheduleするということです。

設定方法

bootstrapModuleのとき、かきのOptionを設定することができます。

platformBrowserDynamic().bootstrapModule(AppModule, {ngZoneEventCoalescing: true})

副作用

このオプションをTrueにしたら、もともと同期のChange Detectionが非同期になりました、普通のアプリケーションには影響がないですが、Google 内部でのEdge Caseで同期のChange Detectionが求めるテストケースがあるらしくて、それ以外が特に既存のアプリケーションには影響ないはずです。
この機能がすでに最新バージョンで使えますので、なにか問題が発見されたら、ぜひAngular RepoにIssueを提出ください。

以上です。

どうもありがとうございました!

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?