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


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

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

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

2 89

2 ответа:

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

detectChanges (): void

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

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

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

1-The детектор изменений отсоединяется от вида (см. отключить)

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

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

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

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

 myFunction(){
   someFunctionThatIsRunByAThirdPartyCode();

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

Примечание:

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

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

 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-Один из @входов компонента был полностью заменен новым значением, или проще говоря, если ссылка на свойство @Input полностью изменилась .

так что если ChangeDetectionStrategy компонента составляет OnPush и тогда у вас есть:

   var obj = {
     name:'Milad'
   };

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

  obj.name = "a new name";

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

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

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

  obj.name = "a new name";

вы должны сделать это:

  this.cd.markForCheck();

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

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

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

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

событий, таких как :

  • клик
  • Keyup
  • событий подписка
  • etc.

короче :

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

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

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

detectChanges

этот используется для запуска обнаружения изменений для дерева компонентов, начиная с компонента, который вы запускаете detectChanges() далее. Таким образом, обнаружение изменений будет выполняться для текущего компонента и всех его дочерних элементов. Angular содержит ссылки на корневое дерево компонентов в ApplicationRef и когда любой асинхронный операция происходит это вызывает обнаружение изменений на этом корневом компоненте через метод обертки 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 для более подробной информации.