Если вы пишете на Angular, то наверняка часто используете хуки жизненного цикла вроде ngOnChanges, ngOnInit и ngOnDestroy. С появлением сигналов и концепции Zoneless (когда Zone.js уже не обязателен) у нас появились более элегантные и читаемые альтернативы.
Давайте разберем, как современный подход позволяет упростить код и избавиться от «шумных» методов жизненного цикла.
1. Вместо ngOnChanges — computed()
Было: классический подход с ngOnChanges
@Component({...})export class PricingComponent implements OnChanges { @Input() price = 0; totalPrice = 0; constructor(private taxService: TaxService) {} ngOnChanges(changes: SimpleChanges) { if (changes['price']) { // При обновлении инпута дергаем сервис this.totalPrice = this.taxService.calculateTotal(this.price); } }}
Стало: реактивно с сигналами
@Component({...})export class PricingComponent { // Сигнальный вход price = input<number>(0); private taxService = inject(TaxService); // Реактивно вычисляем общую цену при каждом изменении price totalPrice = computed(() => this.taxService.calculateTotal(this.price()));}
Плюсы: короче, читабельно, более производительно (вычисляется только когда реально нужно).
2. Отказываемся от ngOnInit в пользу декларативной инициализации
ngOnInit часто использовали как «безопасную» точку, где входные параметры уже гарантированно доступны. С сигналами мы можем инициализировать данные прямо на уровне свойств, реактивно.
Было: инициализация в ngOnInit
@Component({...})export class UserComponent implements OnInit { @Input() userId!: string; userData!: User; constructor(private userService: UserService) {} ngOnInit() { // Ждем, пока Angular установит userId this.userData = this.userService.getUserSync(this.userId); }}
Стало: сигнальный подход
@Component({...})export class UserComponent { userId = input.required<string>(); // обязательный вход private userService = inject(UserService); // Данные пользователя вычисляются реактивно при каждом изменении userId userData = computed(() => this.userService.getUserSync(this.userId()));}
Дополнительный бонус: теперь при изменении
userId(если такое возможно) данные обновятся автоматически — без лишних телодвижений.
3. Убиваем ngOnDestroy с помощью takeUntilDestroyed()
Больше не нужно вручную хранить массивы подписок (Subscription[]) и отписываться в ngOnDestroy. Спасибо оператору takeUntilDestroyed().
Было: ручное управление подпиской
@Component({...})export class AlertComponent implements OnDestroy { private sub: Subscription; constructor(private alertService: AlertService) { this.sub = this.alertService.alerts$ .subscribe(alert => console.log(alert)); } ngOnDestroy() { this.sub.unsubscribe(); }}
Стало: автоматическая отписка через pipe
@Component({...})export class AlertComponent { constructor(private alertService: AlertService) { this.alertService.alerts$ .pipe(takeUntilDestroyed()) // Angular сам отпишет при уничтожении компонента .subscribe(alert => console.log(alert)); }}
Код стал лаконичнее, и риск забыть про unsubscribe исчезает.(А если совсем перейти с RxJS на сигналы, то можно сделать еще проще — но это уже тема для отдельной статьи.)
Итог: жизненные циклы не умерли, но стали специализированным инструментом
Как видите, большинство повседневных задач, где раньше нужны были хуки, теперь решаются через сигналы и computed. Код получается более декларативным, менее связанным с временем жизни компонента и легче для тестирования.
Означает ли это, что жизненные циклы полностью уходят в прошлое? Нет. Они остаются для специфических задач — например, ngAfterViewInit всё ещё незаменим, когда нужна прямая работа с DOM или canvas.
Но как инструмент «на каждый день» — да, хуки успешно заменяются современными реактивными примитивами.
А что думаете вы?
Переход на сигналы — это действительно эволюция или просто ещё один «очередной способ писать то же самое»? Сталкивались ли вы с подводными камнями при миграции с жизненных циклов на computed и takeUntilDestroyed?
Особенно интересно услышать мнение тех, кто уже пробовал Zoneless-подход в боевых проектах:
-
Какие грабли успели собрать?
-
В каких кейсах без старых хуков всё-таки не обойтись?
-
Как оцениваете производительность после перехода?
А если есть чем поделиться или возразить — добро пожаловать в комментарии. Живое обсуждение — лучший способ разобраться в новой парадигме.
ссылка на оригинал статьи https://habr.com/ru/articles/1040488/