Zoneless Angular в продакшене: опыт полного отказа от zone.js и переход на Signals
Как перестать патчить микротаски браузера, снизить TTI и выстроить предсказуемый реактивный граф без магии.
1. Наследие zone.js: почему магия стала бременем
На протяжении долгих лет Angular полагался на библиотеку zone.js. Её задача была простой: перехватывать все асинхронные события в браузере (таймеры, промисы, события DOM, запросы XHR) и запускать проверку изменений по всему дереву компонентов сверху вниз.
Однако эта магия имела высокую цену: monkey-patching глобальных прототипов, раздутый размер бандла, трудности с интеграцией сторонних библиотек и невозможность гранулярного контроля за обновлениями интерфейса.
Когда любое движение мыши или тик таймера провоцирует сканирование всего дерева компонентов, разработчикам приходится прибегать к OnPush, runOutsideAngular и ChangeDetectorRef.markForCheck — это симптомы борьбы со следствием, а не с причиной.
2. Signals: ментальная модель без неявных подписок
Сигналы в современном Angular меняют фундаментальный контракт реактивности. Сигнал — это синхронный геттер значения с подпиской на уровне потребителя. В отличие от потоков RxJS, сигналы всегда хранят текущее значение, не требуют ручной отписки и не создают «zombie subscriptions».
В режиме Zoneless (через provideZonelessChangeDetection()) фреймворк больше не следит за таймерами. Обновление DOM инициируется исключительно сигналами, привязанными к представлению.
3. resource и linkedSignal: реактивные примитивы нового поколения
В Angular появились мощные встроенные примитивы: resource (и httpResource) для работы с асинхронными источниками данных и linkedSignal для синхронизации производного состояния, которое может сбрасываться при изменении родительского значения.
Это позволило избавиться от сотен строк шаблонного кода со связками Subject + switchMap + takeUntilDestroyed, сделав декларативную работу с данными естественной и безопасной.