Почему Angular2 улавливает как изменение ссылки, так и примитивное изменение во время обнаружения изменений даже с установленным флагом OnPush?

Рассмотрим следующий код.

import {Component, OnInit, Input, OnChanges, DoCheck, ChangeDetectionStrategy} from 'angular2/core'

@Component({
  selector: 'child1',
  template: `
    <div>reference change for entire object: {{my_obj1.name}}</div>
    <div>reassign primitive in property of object: {{my_obj2.name}}</div>
    <div>update primitive in property of object: {{my_obj2.num}}</div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1 {

  @Input()
  my_obj1: Object = {'name': ''};

  @Input()
  my_obj2: Object = {'name': '', 'num': 0};

  ngDoCheck() {
    console.log('check from child1');
    console.log(this.my_obj1);
    console.log(this.my_obj2);
  }
}

@Component({
  selector: 'parent',
  template: `
    <div>
      <child1
        [my_obj1]="my_obj1"
        [my_obj2]="my_obj2"
      >
      </child1>
      <button (click)="change_obj1()">
        Change obj1
      </button>

    </div>
    `,
  directives: [Child1]
})
export class App {

  my_obj1: Object = {'name': 'name1'};
  my_obj2: Object = {'name': 'name2', 'num': 0};

  change_obj1() {
    this.my_obj1 = {'name': 'change1'}
    this.my_obj2['name'] = 'change2';
    this.my_obj2['num'] += 1;
  }
}

Из эксперимента, который я провел, вот мое понимание текущей стратегии обнаружения изменений Angular2, может ли кто-нибудь проверить, верно ли это?

  1. Angular2 по умолчанию проверяет равенство значений при обнаружении изменений. Если ChangeDetectionStrategy.OnPush нет, каждая отслеживаемая переменная в дереве компонентов проверяется на равенство значений. Если равенство значений ложно, этот конкретный компонент будет перерисован, а если равенство значений истинно, этот конкретный компонент не будет перерисован.

  2. Если вы добавите ChangeDetectionStrategy.OnPush к компоненту. Поведение меняется следующим образом

    я. Если переменная внутри компонента имеет изменение ссылки, компонент перерисовывается, а дочерний компонент проверяется на обнаружение изменений (его конкретное значение алгоритма обнаружения изменений/проверка ссылки зависит от ChangeDetectionStrategy.OnPush)

    II. Если переменная внутри компонента не имеет ссылки на изменение, компонент не перерисовывается, и дочерний компонент не проверяется на обнаружение изменений, независимо от наличия ChangeDetectionStrategy.OnPush

Это правильная интерпретация?


person testing    schedule 18.04.2016    source источник


Ответы (2)


Я немного переработал ваш плункер: новый плунжер

Поскольку значения примитивов являются неизменяемыми, нет никакой разницы между переназначением и обновлением примитива, который получает новый неизменное значение, поэтому я удалил «обновляющий» код. Кроме того, очень полезно разделить назначение новой ссылки на объект (которая запускает обнаружение изменений) и назначение нового значения примитива (которое не запускает обнаружение изменений). Так что я сделал это также.

Если вы запустите мой Plunker, мы сможем сделать следующие наблюдения:

  1. изменение входного свойства, которое является ссылочным типом в компоненте OnPush, обновит представление компонента. Привязки шаблонов проверяются на наличие изменений. Кроме того, проверяются дочерние компоненты (при условии, что они не используют OnPush).
  2. изменение примитивного свойства, содержащегося в ссылочном типе в компоненте OnPush, не обновит представление компонента. Привязки шаблона не проверяются на наличие изменений. Кроме того, дочерние компоненты не проверяются, независимо от того, используют ли они OnPush или нет.
  3. ngDoCheck() всегда вызывается для первого компонента OnPush, независимо от того, проверяются ли привязки шаблона на изменение или нет. Я нахожу это странным (кто знает, может быть, это ошибка). Таким образом, только потому, что вызывается ngDoCheck(), не обязательно означает, что привязки шаблона проверяются.

Обратите внимание, что при обнаружении изменения привязки шаблона только это изменение распространяется на дочерний компонент или модель DOM (при необходимости, в зависимости от типа привязки). Если изменение привязки привело к изменению DOM, весь компонент не перерисовывается. Обновляются только эти связанные данные DOM, и браузер будет обновлять только этот один элемент DOM. (Это отличается от некоторых других фреймворков, где они перерисовывают весь шаблон, если обнаруживаются какие-либо изменения. Это помогает сделать Angular быстрее.)

person Mark Rajcok    schedule 18.04.2016

Этот пост довольно подробно объясняет это:

http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation

Короче ваши предположения верны. Angular2 должен быть консервативным и проверять равенство значений, то есть он должен выполнять «глубокую проверку» объектов, на которые ссылаются.

С ChangeDetectionStrategy.OnPush компоненты будут обновляться только в том случае, если изменились ссылки на их объекты ввода.

Вот почему неизменяемые объекты могут быть предпочтительными структурами данных — если мы должны обновить объект, компонент теперь ссылается на новый объект. И поэтому angular легко узнать, какие компоненты необходимо обновить.

Эффективное поведение также может быть достигнуто с помощью наблюдаемых с помощью метода ChangeDetectorRef.markForCheck();.

Это объясняется здесь:

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

person nikk wong    schedule 18.04.2016
comment
Можете ли вы объяснить, что вы подразумеваете под глубокой проверкой? - person Mark Rajcok; 19.04.2016
comment
Он должен проверять объект и все его свойства. Однако это быстрее, чем версия angular 1, которая будет делать что-то вроде oldValue.prop1 === newValue.prop1, потому что angular2 создает классы детектора изменений во время выполнения, которые являются мономорфными (проверки angular1 полиморфны). Подробнее –› youtube.com/watch?v=jvKGQSFQf10 - person nikk wong; 19.04.2016
comment
Это то, что я думал, что вы имели в виду, но я хотел проверить, прежде чем комментировать. Насколько я понимаю, Angular 2 не проверяет все свойства объектов, следовательно, он не выполняет глубокую проверку объектов. Скорее, он проверяет только те свойства, которые используются в привязках шаблонов. Итак, если у нас есть объект с 20 свойствами, но только одно из них имеет привязку к шаблону, тогда связанный с ним класс детектора изменений проверяет только это одно свойство. Этот блог говорит то же самое: blog.jhades .org/how-does-angular-2-change-detection-really-work/ - person Mark Rajcok; 19.04.2016