В чем разница между markForCheck () и detectChanges()
в чем разница между ChangeDetectorRef.markForCheck()
и ChangeDetectorRef.detectChanges()
?
Я только найдена информация о SO что касается разницы между NgZone.run()
, но не между этими двумя функциями.
для ответов только со ссылкой на doc, пожалуйста, проиллюстрируйте некоторые практические сценарии, чтобы выбрать один над другим.
2 ответов
из docs:
detectChanges (): void
это означает, что если есть случай, когда какая-либо вещь внутри вашей модели (вашего класса) изменилась, но она не отразила представление, вам может потребоваться уведомить Angular, чтобы обнаружить эти изменения (обнаружить локальные изменения) и обновить представление.
возможные сценарии могут быть :
1-The детектор изменений отсоединяется от вида (см. отключить )
2 - обновление произошло, но оно не было внутри угловой зоны, поэтому Angular не знает об этом.
например, когда сторонняя функция обновила вашу модель, и вы хотите обновить представление после этого.
someFunctionThatIsRunByAThirdPartyCode(){
yourModel.text = "new text";
}
поскольку этот код находится за пределами зоны Angular (вероятно), вам, скорее всего, нужно обязательно обнаружить изменения и обновить представление , таким образом :
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 ссылка, следовательно, изменение обнаружение не будет выполняться, поэтому представление не отражает обновление/мутацию.
в этом случае вы должны вручную сказать Angular, чтобы проверить и обновить представление (markForCheck);
Итак, если вы это сделали:
obj.name = "a new name";
вам нужно сделать это:
this.cd.markForCheck();
скорее, bellow вызовет обнаружение изменений для запуска:
obj = {
name:"a new name"
};
который полностью заменил предыдущий obj новым {}
;
2-An событие сработало, как щелчок или что-то подобное, или любой из дочерних компонентов испустил событие.
событий, таких как :
- клик
- Keyup
- событий подписка
- etc.
короче :
использовать
detectChanges()
когда вы обновили модель после запуска angular, это обнаружение изменений или если обновление не было в angular world в все.использовать
markForCheck()
если вы используете OnPush и обходитеChangeDetectionStrategy
путем мутации некоторых данных или вы обновили модель внутри setTimeout;
самая большая разница между этими двумя является то, что detectChanges()
фактически запускает обнаружение изменений, в то время как markForCheck()
не вызывает обнаружение изменений.
detectChanges
этот используется для запуска обнаружения изменений для дерева компонентов, начиная с компонента, который вы запускаете detectChanges()
on. Таким образом, обнаружение изменений будет выполняться для текущего компонента и всех его дочерних элементов. 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 для получения более подробной информации.