← Назад к вопросам

В чем разница между onPush и Default в рамках стратегий проверки изменений?

1.8 Middle🔥 141 комментариев
#Архитектура и паттерны#Оптимизация и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между OnPush и Default стратегиями проверки изменений

Stragey проверки изменений (Change Detection Strategy) в Angular контролирует, когда и как компонент должен проверяться на изменения. Есть две основные стратегии: Default и OnPush.

Default стратегия

Default — это стандартная стратегия, которая проверяет компонент и все его потомки при любых изменениях в приложении.

import { Component } from '@angular/core';

@Component({
  selector: 'app-user',
  template: `<h1>{{ user.name }}</h1>`,
  // changeDetection: ChangeDetectionStrategy.Default // По умолчанию
})
export class UserComponent {
  user = { name: 'John Doe' };
}

Характеристики Default:

  • Проверяется всегда: при любом событии (клик, таймер, HTTP запрос)
  • Проверяются все компоненты: целое дерево компонентов
  • Простая логика: не нужно думать о том, когда обновлять
  • Неэффективно: проверяет даже когда не нужно
  • Медленнее: на больших приложениях падает производительность

OnPush стратегия

OnPush — оптимизированная стратегия, которая проверяет компонент только когда:

  • Изменились входные свойства (Input)
  • Произошло событие внутри компонента
  • Явно вызвана ChangeDetectorRef.markForCheck()
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-user',
  template: `<h1>{{ user.name }}</h1>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
  @Input() user: { name: string };
}

Характеристики OnPush:

  • Проверяется редко: только при изменении Input свойств
  • Проверяется компонент и потомки: другие компоненты не затрагиваются
  • Требует дисциплины: нужно правильно передавать данные
  • Более эффективно: пропускаются ненужные проверки
  • Быстрее: на больших приложениях значительно улучшается производительность

Практический пример

// app.component.ts (Parent)
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="increment()">Увеличить счётчик: {{ counter }}</button>
    <app-user [user]="user"></app-user>
  `
})
export class AppComponent {
  counter = 0;
  user = { name: 'John' };

  increment() {
    this.counter++; // Не меняем user
  }
}

// user.component.ts (Child) с Default стратегией
@Component({
  selector: 'app-user',
  template: `<h1>{{ user.name }}</h1>`,
  changeDetection: ChangeDetectionStrategy.Default
})
export class UserComponentDefault {
  @Input() user: { name: string };
}

// user.component.ts (Child) с OnPush стратегией
@Component({
  selector: 'app-user',
  template: `<h1>{{ user.name }}</h1>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponentOnPush {
  @Input() user: { name: string };
}

Когда кликнем на кнопку "Увеличить счётчик":

  • Default: UserComponent будет проверен (хотя user не изменился)
  • OnPush: UserComponent не будет проверен (так как user не изменился)

Важно: Immutable подход

OnPush требует неизменяемости (immutability) данных. Это означает, что нужно создавать новые объекты, а не изменять существующие:

// НЕПРАВИЛЬНО с OnPush (не сработает)
this.user.name = 'Jane'; // Ссылка осталась той же

// ПРАВИЛЬНО с OnPush (сработает)
this.user = { ...this.user, name: 'Jane' }; // Новый объект
// или
this.user = Object.assign({}, this.user, { name: 'Jane' });

Почему? Потому что Angular с OnPush проверяет ссылку на объект, а не содержимое. Если ссылка не изменилась, Angular не проверяет компонент.

Ручная проверка с ChangeDetectorRef

Иногда нужно явно попросить Angular проверить компонент:

import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-timer',
  template: `<p>Время: {{ time }}</p>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TimerComponent implements OnInit {
  time: number = 0;

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    setInterval(() => {
      this.time++;
      // Явно просим Angular проверить компонент
      this.cdr.markForCheck();
    }, 1000);
  }
}

Сравнительная таблица

СвойствоDefaultOnPush
Когда проверяетсяВсегдаТолько при изменении Input или события
ЭффективностьНизкаяВысокая
Требует immutabilityНетДа
СложностьПростаяСложнее
ПроизводительностьМедленнееБыстрее
Подходит дляПростых приложенийБольших приложений

Рекомендации

Используй Default когда:

  • Приложение маленькое
  • Нет проблем с производительностью
  • Данные меняются часто и непредсказуемо
  • Не уверен в своих силах

Используй OnPush когда:

  • Приложение большое и сложное
  • Есть проблемы с производительностью
  • Компонент получает данные через @Input
  • Можешь гарантировать immutability
  • Нужна максимальная оптимизация

Главная разница

Default проверяет компонент при любом изменении в приложении, что неэффективно.

OnPush проверяет компонент только когда:

  • Изменились входные свойства (Input)
  • Произошло событие в компоненте
  • Явно вызвана проверка

ОнПush требует иммутабельного подхода (создание новых объектов вместо изменения существующих).