How does Angular 2 change detection work?

后端 未结 2 1092
不知归路
不知归路 2020-11-30 01:06

In Angular 1, change detection was by dirty checking the $scope hierarchy. We would implicitly or explicitly create watchers in our templates, controllers or components.

2条回答
  •  刺人心
    刺人心 (楼主)
    2020-11-30 01:44

    Zone.js

    Changes happen as a reaction to something, so in this respect they are asynchronous. They are caused by asynchronous actions, and in the browser world those are Events. To intercept those events angular uses zone.js, which patches JavaScript call stack (I beleive, someone correct me if I'm wrong) and exposes hooks that can be used to take other actions.

    function angular() {...}
    zone.run(angular);
    

    If you imagine this angular function is the entire Angular, this would be how it is run in zone. By doing so Events can be intercepted and if they are triggered we can assume changes happen, and listen/watch for them.

    ApplicationRef

    In reality ApplicationRef creates the zone:

    /**
     * Create an Angular zone.
     */
    export function createNgZone(): NgZone {
      return new NgZone({enableLongStackTrace: assertionsEnabled()});
    }
    

    and class NgZone is created with few event emitters:

      this._onTurnStartEvents = new EventEmitter(false);
      this._onTurnDoneEvents = new EventEmitter(false);
      this._onEventDoneEvents = new EventEmitter(false);
      this._onErrorEvents = new EventEmitter(false);
    

    that it exposes to the outside world via getters:

      get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
      get onTurnDone() { return this._onTurnDoneEvents; }
      get onEventDone() { return this._onEventDoneEvents; }
      get onError() { return this._onErrorEvents; }
    

    When ApplicationRef is created it subscribes to the zone's events, specifically onTurnDone():

    this.zone.onTurnDone
      .subscribe(() => this.zone.run(() => this.tick());
    

    Changes

    When events are triggered tick() function is run which loops through every component:

      this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
    

    and detects changes based on components' ChangeDetectionStrategy. Those changes are collected as an array of SimpleChange objects:

    addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
      if (isBlank(changes)) {
        changes = {};
      }
      changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
      return changes;
    }
    

    witch is available for us through onChanges interface:

    export interface OnChanges { 
      ngOnChanges(changes: {[key: string]: SimpleChange}); 
    }
    

提交回复
热议问题