В чем разница между markForCheck и detectChanges в Angular Change Detection?
Что проверяют
Понимаете ли вы, как Angular решает, какие компоненты проверить, и что именно делает каждый метод ChangeDetectorRef. Следом почти всегда спрашивают про стратегию OnPush и про то, как всё это меняется с сигналами и zoneless-режимом.
Как отвечать
Начните с разницы по сути: один метод помечает, другой проверяет.
markForCheck(): сам ничего не проверяет. Помечает текущее представление и всех предков до корня как «грязные», чтобы ближайший цикл обнаружения изменений их не пропустил. Нужен компонентам сOnPush, когда данные поменялись не через@Inputи не через событие в шаблоне: подписка,setTimeout, сообщение из сокета;detectChanges(): синхронно, прямо сейчас, запускает проверку этого компонента и его поддерева. Предков не помечает и следующего цикла не ждёт;asyncpipe: внутри вызываетmarkForCheck()на каждое новое значение, поэтому с ним ручные вызовы обычно не нужны.
Практическое правило: по умолчанию берите markForCheck(), он дешевле и работает в общем ритме приложения. detectChanges() оставьте для случаев, когда результат нужен немедленно в том же тике, например чтобы измерить DOM сразу после изменения данных, или для компонента, отключённого от дерева через detach().
Хорошо добавить пару слов про современный Angular. Сигналы, прочитанные в шаблоне, сами помечают компонент на проверку, поэтому ручных вызовов становится меньше. А в zoneless-режиме (provideZonelessChangeDetection()) zone.js больше не угадывает момент проверки, и её запускают именно уведомления: изменение сигнала, событие в шаблоне, markForCheck().
Где спотыкаются
Говорят, что markForCheck() запускает проверку. Он только ставит флаг, и если цикла после этого не будет, экран не обновится.
Лечат ExpressionChangedAfterItHasBeenCheckedError вызовом detectChanges() в ngAfterViewInit. Ошибка пропадает, причина остаётся: данные меняются во время проверки, и чинить нужно поток данных.
Вызывают detectChanges() в цикле или на каждое событие частого потока. Это синхронный рендер поддерева на каждый вызов, и интерфейс начинает подтормаживать.