Эффекты¶
30.09.2026
Сигналы полезны тем, что сообщают заинтересованным потребителям об изменении. Эффект — операция, которая выполняется, когда меняется значение одного или нескольких сигналов. Эффект создают функцией effect:
1 2 3 4 5 | |
Эффект всегда выполняется хотя бы один раз. При запуске он запоминает прочитанные значения сигналов. Когда любое из них меняется, эффект запускается снова. Как и вычисляемые сигналы, эффект следит за зависимостями динамически и учитывает только сигналы, прочитанные в последнем запуске.
Эффекты всегда выполняются асинхронно, во время обнаружения изменений.
Когда нужны эффекты¶
Эффект — последнее средство. Производные значения считайте через computed(), а значения, которые и выводятся, и задаются вручную, — через linkedSignal(). Если эффект только копирует данные из сигнала в сигнал, источник состояния стоит поднять выше и заменить эффект на computed() или linkedSignal(). Лучшее место для эффекта — синхронизация состояния сигнала с императивным API, который сам сигналов не знает.
Нет ситуаций, где эффект просто хорош. Есть только ситуации, где он уместен.
- Запись значений сигналов в журнал: для аналитики или как средство отладки.
- Синхронизация данных с разными хранилищами:
window.localStorage, хранилище сессии, куки и так далее. - Своё поведение DOM, которое не выразить синтаксисом шаблона.
- Своя отрисовка в элемент
<canvas>, в библиотеку графиков или в другую стороннюю библиотеку интерфейса.
Когда эффекты не нужны
Не разносите эффектом изменения состояния. Так возникают ошибки ExpressionChangedAfterItHasBeenChecked, бесконечные циклы обновлений и лишние проходы обнаружения изменений.
Состояние, которое зависит от другого состояния, описывайте вычисляемым сигналом computed.
Контекст инъекции¶
По умолчанию effect() создают только в контексте инъекции, где доступна функция inject. Проще всего выполнить это условие, вызвав effect в конструкторе компонента, директивы или сервиса:
1 2 3 4 5 6 7 8 9 10 11 | |
Чтобы создать эффект вне конструктора, передайте в параметры effect инжектор Injector:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Выполнение эффектов¶
В зависимости от контекста создания Angular неявно задаёт эффектам одно из двух поведений.
«Эффект представления» — это effect, созданный при создании компонента. Сюда входят эффекты сервисов, привязанных к инжекторам компонентов. «Корневой эффект» создаётся при создании сервиса, предоставленного в корне.
Выполнение обоих видов effect привязано к обнаружению изменений.
- «Эффекты представления» выполняются до того, как обнаружение изменений проверит соответствующий компонент.
- «Корневые эффекты» выполняются до проверки всех компонентов.
В обоих случаях, если за время выполнения эффекта изменилась хотя бы одна его зависимость, эффект запустится снова, и только потом обнаружение изменений пойдёт дальше.
Уничтожение эффектов¶
Когда компонент или директива уничтожается, Angular сам убирает связанные эффекты.
effect создают в двух контекстах, и от контекста зависит, когда он уничтожится:
- «Эффект представления» уничтожается вместе с компонентом.
- «Корневой эффект» уничтожается вместе с приложением.
Эффект возвращает EffectRef. Метод destroy этой ссылки уничтожает эффект вручную. Вместе с параметром manualCleanup при создании эффекта это отключает автоматическую очистку. Не забудьте уничтожить такой эффект, когда он больше не нужен.
Функции очистки эффекта¶
Когда компонент или директива уничтожается, Angular сам убирает связанные эффекты. Эффект может запустить долгую операцию. Её нужно отменить, если эффект уничтожен или запускается снова, пока первая операция не закончилась. Функция эффекта может принять первым параметром функцию onCleanup. Через onCleanup регистрируют обратный вызов. Он вызывается перед следующим запуском эффекта или при его уничтожении.
1 2 3 4 5 6 7 8 9 10 11 | |
Побочные эффекты на элементах DOM¶
Функция effect — универсальный способ выполнить код в ответ на изменение сигнала. Но она запускается до того, как Angular обновит DOM. Иногда DOM нужно осмотреть или изменить вручную либо подключить стороннюю библиотеку с прямым доступом к DOM.
Для этого есть afterRenderEffect. Она устроена как effect, но запускается после того, как Angular закончил отрисовку и записал изменения в DOM.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
В этом примере afterRenderEffect обновляет график, который создала сторонняя библиотека.
Чтобы следить за изменениями DOM, afterRenderEffect часто не нужен. Если можно, берите ResizeObserver, MutationObserver и IntersectionObserver, а не effect или afterRenderEffect.
Фазы отрисовки¶
Чтение и изменение DOM влияет на производительность: например, вызывает лишние перекомпоновки.
Чтобы упорядочить такие операции, afterRenderEffect даёт четыре фазы: обратные вызовы группируют по фазам и выполняют в выгодном порядке.
Фазы такие:
| Фаза | Описание |
|---|---|
earlyRead | Читайте DOM в этой фазе до последующей записи, например чтобы посчитать свою раскладку, которую браузер сам не умеет. Если чтение может подождать, лучше фаза read. |
write | Пишите в DOM в этой фазе. В этой фазе DOM нельзя читать. |
mixedReadWrite | В этой фазе DOM читают и пишут одновременно. Не берите её, если работу можно разнести по другим фазам. |
read | Читайте DOM в этой фазе. В этой фазе в DOM нельзя писать. |
Такое разбиение защищает от лишних перекомпоновок: операции с DOM идут в безопасном и экономном порядке.
Фазы задают объектом с обратным вызовом на каждую фазу и передают его в afterRenderEffect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Если фазу не указать, afterRenderEffect выполняет обратные вызовы на фазе mixedReadWrite. Лишние перекомпоновки DOM могут замедлить приложение.
Выполнение фаз¶
Обратный вызов earlyRead не получает параметров. Каждая следующая фаза получает результат предыдущей как сигнал Signal. Так работу согласуют между фазами.
Фазы выполняются в таком порядке:
earlyReadwritemixedReadWriteread
Если фаза меняет сигнал, за которым следит afterRenderEffect, затронутые фазы выполняются снова.
Очистка¶
Каждая фаза получает функцию очистки аргументом. Очистка выполняется, когда afterRenderEffect уничтожается, и перед повторным запуском эффектов фазы.
Оговорки серверного рендеринга¶
afterRenderEffect, как и afterNextRender с afterEveryRender, выполняется только на клиенте.
К моменту обратного вызова компонент не обязательно гидратирован. Напрямую читать и писать DOM и раскладку нужно осторожно.
Источник: https://angular.dev/guide/signals/effect