Подтвердить что ты не робот

Какая разница между markForCheck() и detectChanges()

В чем разница между ChangeDetectorRef.markForCheck() и ChangeDetectorRef.detectChanges()?

Я только нашел информацию о SO относительно разницы между NgZone.run(), но не между этими двумя функциями.

Для ответов со ссылкой только на документ, пожалуйста, проиллюстрируйте некоторые практические сценарии, чтобы выбрать один из других.

4b9b3361

Ответ 1

Из документов:

detectChanges(): void

Проверяет детектор изменений и его дочерние элементы.

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

Возможные сценарии могут быть:

1- Детектор изменений отсоединен от вида (см. Отсоединение)

2- Обновление произошло, но оно не было в угловой зоне, поэтому Angular не знает об этом.

Например, когда сторонняя функция обновила вашу модель, и вы хотите обновить представление после этого.

 someFunctionThatIsRunByAThirdPartyCode(){
     yourModel.text = "new text";
 }

Поскольку этот код находится за пределами угловой зоны (вероятно), вам, скорее всего, необходимо убедиться, что обнаружены изменения и обновлено представление, таким образом:

 myFunction(){
   someFunctionThatIsRunByAThirdPartyCode();

   // Let detect the changes that above function made to the model which Angular is not aware of.
    this.cd.detectChanges();
 }

ПРИМЕЧАНИЕ:

Есть и другие способы заставить работать выше, иными словами, есть и другие способы внести это изменение в цикл угловых изменений.

** Вы можете обернуть эту стороннюю функцию внутри zone.run:

 myFunction(){
   this.zone.run(this.someFunctionThatIsRunByAThirdPartyCode);
 }

** Вы можете обернуть функцию внутри setTimeout:

myFunction(){
   setTimeout(this.someFunctionThatIsRunByAThirdPartyCode,0);
 }

3- Есть также случаи, когда вы обновляете модель после завершения change detection cycle, где в этих случаях вы получаете эту страшную ошибку:

"Выражение изменилось после того, как оно было проверено";

Обычно это означает (на языке Angular2):

Я увидел изменение в вашей модели, которое было вызвано одним из моих принятых способов (события, запросы XHR, setTimeout и...), а затем я запустил обнаружение изменений, чтобы обновить ваше представление, и закончил его, но затем появился другой функция в вашем коде, которая снова обновила модель, и я не хочу снова запускать обнаружение изменений, потому что больше нет грязной проверки, как AngularJS: D и мы должны использовать односторонний поток данных!

Вы обязательно столкнетесь с этой ошибкой: P.

Несколько способов это исправить:

1- Правильный способ: убедитесь, что обновление находится в цикле обнаружения изменений (обновления Angular2 - это односторонний поток, который происходит один раз, не обновляйте модель после этого и не перемещайте код в лучшее место/время).

2- Ленивый способ: после того, как это обновление запустится detectChanges(), чтобы сделать angular2 счастливым, это определенно не лучший способ, но, как вы спросили, каковы возможные сценарии, это один из них.

Таким образом, вы говорите: я искренне знаю, что вы запустили обнаружение изменений, но я хочу, чтобы вы сделали это снова, потому что мне нужно было что-то обновить на лету после того, как вы закончили проверку.

3- Поместите код в setTimeout, потому что setTimeout пропатчен зоной и запустит detectChanges после его завершения.


Из документов

markForCheck() : void

Отмечает всех предков ChangeDetectionStrategy как проверяемые.

Это главным образом необходимо, когда ChangeDetectionStrategy вашего компонента - OnPush.

OnPush означает, что запускать обнаружение изменений можно только в том случае, если произошло что-то из этого:

1- Один из @inputs компонента был полностью заменен новым значением или, проще говоря, если ссылка на свойство @Input полностью изменилась.

Итак, если ChangeDetectionStrategy вашего компонента - OnPush, и тогда у вас есть:

   var obj = {
     name:'Milad'
   };

И затем вы обновляете/мутируете, как:

  obj.name = "a new name";

Это не будет обновлять ссылку на объект, следовательно, обнаружение изменений не будет запущено, поэтому представление не отражает обновление/мутацию.

В этом случае вы должны вручную указать Angular проверять и обновлять представление (markForCheck);

Так что, если вы сделали это:

  obj.name = "a new name";

Вам нужно сделать это:

  this.cd.markForCheck();

Скорее, ниже приведёт к запуску обнаружения изменений:

    obj = {
      name:"a new name"
    };

Который полностью заменил предыдущий объект новым {};

2- Событие сработало, например, щелчок или что-то подобное, или любой из дочерних компонентов выпустил событие.

События как:

  • Нажмите
  • KeyUp
  • События подписки
  • и т.п.

Итак, вкратце:

  • Используйте detectChanges() когда вы обновили модель после того, как angular запустил ее обнаружение изменений, или если обновление вообще не было в angular.

  • Используйте markForCheck() если вы используете OnPush и markForCheck() ChangeDetectionStrategy, изменяя некоторые данные, или вы обновили модель внутри setTimeout;

Ответ 2

Самое большое различие между ними состоит в том, что detectChanges() фактически запускает обнаружение изменений, а markForCheck() не вызывает обнаружение изменений.

detectChanges

Этот используется для запуска обнаружения изменений для дерева компонентов, начиная с компонента, который вы запускаете detectChanges() on. Таким образом, обнаружение изменений будет выполняться для текущего компонента и всех его дочерних элементов. Angular содержит ссылки на дерево корневых компонентов в ApplicationRef, и когда происходит любая операция async, он запускает обнаружение изменений этого корневого компонента с помощью метода обертки tick():

@Injectable()
export class ApplicationRef_ extends ApplicationRef {
  ...
  tick(): void {
    if (this._runningTick) {
      throw new Error('ApplicationRef.tick is called recursively');
    }

    const scope = ApplicationRef_._tickScope();
    try {
      this._runningTick = true;
      this._views.forEach((view) => view.detectChanges()); <------------------

view вот представление корневого компонента. Могут быть много корневых компонентов, как я описал в Каковы последствия самонастройки нескольких компонентов.

@milad описал причины, по которым вам потенциально может потребоваться автоматическое обнаружение изменений.

markForCheck

Как я уже сказал, этот парень вообще не вызывает обнаружение изменений. Он просто идет вверх от текущего компонента к корневому компоненту и обновляет состояние представления до ChecksEnabled. Вот исходный код:

export function markParentViewsForCheck(view: ViewData) {
  let currView: ViewData|null = view;
  while (currView) {
    if (currView.def.flags & ViewFlags.OnPush) {
      currView.state |= ViewState.ChecksEnabled;  <-----------------
    }
    currView = currView.viewContainerParent || currView.parent;
  }
}

Фактическое обнаружение изменений для компонента не запланировано, но когда это произойдет в будущем (либо как часть текущего, либо следующего цикла CD), представления родительского компонента будут проверяться, даже если у них есть деблокированные детекторы изменений. Изменяемые детекторы можно отключить либо с помощью cd.detach(), либо путем определения стратегии обнаружения изменений OnPush. Все собственные обработчики событий отмечают все представления родительских компонентов для проверки.

Этот подход часто используется в привязке жизненного цикла ngDoCheck. Вы можете прочитать больше в Если вы считаете, что ngDoCheck означает, что ваш компонент проверяется - прочитайте эту статью.

См. также Все, что вам нужно знать об обнаружении изменений в Angular для более подробной информации.