Модуль 3 · урок 2 з 11
Default проти OnPush
Стратегія перевірки — це домовленість між тобою й Angular про те, коли компонент
взагалі варто дивитись. За замовчуванням він перевіряється завжди. З OnPush —
лише коли для цього є привід. Це прямий пункт із таблиці ліда й найпростіша оптимізація,
яка існує в Angular.
Default: перевіряти все й завжди
За замовчуванням кожен компонент потрапляє в кожен цикл перевірки. Якщо в застосунку двісті компонентів, а користувач просто повів мишею — Angular звірить шаблон із класом двісті разів. Здебільшого це швидко й непомітно. Проблеми починаються там, де вузлів багато або де в шаблонах є обчислення.
OnPush: перевіряти, коли є привід
@Component({
selector: 'app-task-card',
changeDetection: ChangeDetectionStrategy.OnPush,
// …
})
Компонент із OnPush перевіряється рівно в пʼяти випадках:
- Змінилось посилання на будь-який
@Input()— саме посилання, а не вміст. - Подія з самого компонента чи його шаблону:
(click),(input), хост-слухач. asyncpipe у шаблоні отримав нове значення.- Змінився сигнал, який читається в цьому шаблоні.
- Хтось явно викликав
markForCheck().
Ключове слово — посилання
// ❌ дитина з OnPush нічого не помітить
this.tasks.push(newTask);
this.selected.status = 'done';
// ✅ нове посилання — дитина перевіриться
this.tasks = [...this.tasks, newTask];
this.selected = { ...this.selected, status: 'done' };
Це найчастіша причина «я поставив OnPush і все зламалось». Насправді нічого не зламалось: компонент чесно повідомив, що з його точки зору дані ті самі — бо посилання те саме. Наступний урок цілком про це.
Що з подіями
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<button (click)="count = count + 1">{{ count }}</button>
`,
})
export class Counter {
count = 0; // звичайне поле, не сигнал
}
Це працює навіть із OnPush: подія прийшла з шаблону самого компонента,
тож він позначається для перевірки. Пастка виникає в іншому місці — коли значення змінюється
ззовні, з колбека, таймера чи підписки. Тоді приводу немає, і екран лишається старим.
Як позначається «брудний» шлях
Коли компонент отримує привід перевіритись, Angular позначає не лише його, а весь ланцюжок
до кореня. Інакше обхід просто не дійшов би до нього: батьки з OnPush зупинили б
спуск раніше.
Ставити OnPush вибірково майже безглуздо. Якщо батько лишився на
Default, він однаково перевіряється щоразу — і гілка не пропускається.
Тому правило просте: OnPush на всіх компонентах, з першого дня. І саме тому в модулі 2
ми ввімкнули ESLint-правило, яке про це нагадує.
- Таблиця на тисячу рядків не тормозить при наведенні миші.
- У профайлері видно кілька вузлів замість усього дерева.
- Код стає передбачуваним: оновлення лише через нові обʼєкти.
- Це прямий критерій із таблиці, який легко показати.
- Мутації масивів і обʼєктів перестають бути видимими.
- Оновлення з
setTimeoutчи стороннього колбека не доходить до екрана. - Ручний
subscribeбезmarkForCheckнічого не малює. - Спокуса «полагодити» все через
detectChanges()— про це урок 4.
Найкоротша відповідь на співбесіді
«OnPush означає, що компонент перевіряється не в кожному циклі, а лише коли
змінилось посилання на input, прийшла подія з його шаблону, спрацював async pipe,
змінився сигнал у шаблоні або хтось викликав markForCheck. Через це працювати
треба з незмінними даними: не мутувати масив, а створювати новий.»