State Management
Сигналы как парадигма
Компонент дашборда показывает счётчик уведомлений в шапке. Приходит новое уведомление, число меняется с 3 на 4. В подходе с виртуальным DOM React перевызывает функцию компонента, строит новое дерево, сравнивает его со старым и находит, что поменялся ровно один текстовый узел. В подходе с сигналами обновляется именно тот один текстовый узел, без перевызова компонента и без диффинга. Solid, Angular, Preact и Vue пришли к одной идее: хранить значение в реактивной ячейке, которая сама знает, кто на неё подписан, и трогать в DOM только то, что реально зависит от изменения.
- SolidJS: компонент-функция выполняется один раз, дальше обновляется только конкретный DOM-узел, привязанный к сигналу
- Angular с версии 16+: signal(), computed(), effect() как встроенный примитив реактивности вместо проверки всего дерева через Zone.js
- Preact Signals: @preact/signals интегрируется в React и обновляет точечно, минуя часть перерисовок
- Vue: ref() и computed() это фактически сигналы, на них держится вся реактивность Vue 3
- Svelte 5 runes ($state, $derived): тот же fine-grained подход с компиляцией реактивности в точечные обновления
Предварительные знания
- Обзор парадигм реактивности: pull против push, VDOM против точечных обновлений
- Понимание, что такое ре-рендер компонента и почему лишние перерисовки стоят процессорного времени
- Базовое представление о подписке: одно значение, много слушателей
Откуда выросла идея сигналов
Идея реактивной ячейки с автоматическим отслеживанием зависимостей не нова. Knockout.js (2010) уже предлагал observable и computed с автоподпиской. Библиотека S.js Адама Хейла (около 2013) довела fine-grained реактивность до примитива и повлияла на Райана Карниато, автора SolidJS (2018). В Solid вычисления сами регистрируют, какие сигналы они читают, и пересчитываются только при изменении именно этих сигналов. В 2023 году Angular взял ту же модель и добавил signal/computed/effect в ядро. Тогда же оформилось предложение TC39 Signals: попытка договориться о едином примитиве реактивности на уровне самого языка. На июнь 2026 года это предложение всё ещё обсуждается в комитете и не входит в стандарт JavaScript.
Что такое сигнал
Сигнал это контейнер для одного значения с двумя свойствами. Первое: чтение сигнала регистрирует читателя, то есть текущее реактивное вычисление запоминается как зависимый. Второе: запись нового значения оповещает всех зарегистрированных читателей. За счёт этого система сама знает граф зависимостей и не нуждается в ручных подписках или в сравнении деревьев.
В обоих случаях чтение это вызов функции, а не доступ к полю. Это сделано намеренно: момент вызова это и есть точка, в которой система фиксирует зависимость. Vue прячет тот же механизм за свойством value у ref, но суть одинаковая - чтение отслеживается, запись оповещает.
- Обычная переменная — let count = 0. Изменение count никого не уведомляет. UI обновится только если что-то снаружи решит перечитать значение
- Сигнал — count это реактивная ячейка. Чтение запоминает читателя, запись его будит. Связь значение-UI поддерживается автоматически
Что отличает сигнал от обычной переменной?
Fine-grained реактивность против перерисовки компонента
В модели с виртуальным DOM изменение состояния перевызывает функцию компонента целиком, строит новое дерево элементов и сравнивает его со старым, чтобы найти минимальный набор правок DOM. В модели сигналов компонент-функция выполняется один раз при создании. Дальше каждый сигнал связан напрямую с тем участком DOM, который его читает, и при изменении правится только этот участок. Перевызова компонента и диффинга нет.
Ключевое следствие: в Solid нет смысла говорить о лишних ре-рендерах компонента, потому что тело компонента не перевызывается на каждое изменение. Обновляется только конкретный связанный узел DOM. Это противоположно React, где основная работа по оптимизации это не давать функции компонента перерисовываться зря.
| Свойство | VDOM (React) | Сигналы (Solid/Angular/Preact) |
|---|---|---|
| Что происходит при изменении | Перевызов функции компонента, новое дерево, дифф | Правка конкретного DOM-узла, читающего сигнал |
| Тело компонента | Выполняется на каждый ре-рендер | Выполняется один раз при создании |
| Гранулярность обновления | Компонент целиком, дальше дифф | Отдельный узел или атрибут |
| Главная оптимизация | Не перерисовываться зря (memo, useMemo) | Уже точечно по устройству модели |
Preact Signals доносит ту же выгоду внутрь React-подобной среды: значение сигнала, вставленное прямо в JSX, обновляет текстовый узел минуя перерисовку компонента. Это компромисс между привычной моделью React и точечностью сигналов.
Чем обновление DOM при изменении сигнала в Solid отличается от обновления в модели VDOM?
Производные значения и эффекты
Сигнал редко живёт в одиночку. Поверх него строят два производных примитива. Computed это значение, вычисленное из других сигналов: оно само отслеживает, какие сигналы прочитало, кэширует результат и пересчитывается лениво только при изменении одного из источников. Effect это побочное действие, которое перезапускается, когда меняется прочитанный им сигнал, например запись в DOM, лог или отправка запроса.
Важно, что computed ленив и кэширован. Если total никто не читает, пересчёт не происходит. Если price и qty не менялись, повторное чтение total отдаёт закэшированное значение без повторного умножения. Граф зависимостей строится автоматически по факту чтения, поэтому добавление нового источника внутрь computed не требует ничего объявлять вручную.
Эффект это место для побочных действий, а не для вычисления нового состояния. Запись одного сигнала внутри эффекта, который читает другой сигнал, легко приводит к каскадам перезапусков и циклам. Для получения значения из значений берут computed, а effect оставляют для выхода во внешний мир: DOM, сеть, логи.
- signal: источник изменяемого значения
- computed: ленивое кэшированное производное значение от других сигналов
- effect: побочное действие, перезапускаемое при изменении прочитанных сигналов
Производное значение объявлено через computed и его в текущий момент никто не читает. Что происходит при изменении исходного сигнала?
Предложение TC39 Signals: статус на 2026 год
Сегодня каждый фреймворк несёт собственную реализацию сигналов: Solid свою, Angular свою, Preact свою, Vue прячет за ref. Реализации похожи по идее, но несовместимы по API и не переносятся между фреймворками. Предложение TC39 Signals пытается это исправить и ввести единый примитив реактивности прямо в язык, чтобы библиотеки могли строиться на общей основе.
На июнь 2026 года предложение TC39 Signals находится на стадии обсуждения в комитете и не входит в стандарт JavaScript. Это значит: нет встроенного Signal в браузере, нет гарантии финального API, и закладываться на него в продакшене как на стандарт пока нельзя.
Практический вывод такой. Сигналы как парадигма уже выиграли: точечная реактивность есть в большинстве свежих фреймворков и решает реальную задачу обновления UI без лишней работы. При этом единого языкового стандарта пока нет, поэтому код с сигналами остаётся привязанным к конкретному фреймворку, а не к самому JavaScript. Стандартизация может это поменять, но утверждать сроки преждевременно.
Каков статус сигналов в JavaScript на июнь 2026 года?
Связь с другими темами
Сигналы это парадигма, на которой стоят сразу несколько стор-решений:
- Реактивность Vue и Pinia — ref и computed во Vue это сигналы под другим именем, и Pinia строит стор поверх них
- NgRx SignalStore — SignalStore в Angular это стор, целиком построенный на встроенных сигналах вместо потока action-reducer
- Stores во Svelte — Реактивные store и runes Svelte решают ту же задачу точечного обновления, что и сигналы
Итог
- Сигнал это реактивная ячейка значения, которая отслеживает своих читателей и оповещает только их при изменении
- Fine-grained реактивность обновляет конкретный DOM-узел напрямую, без перевызова компонента и без диффинга виртуального дерева
- Производные значения через computed пересчитываются лениво и только когда изменился один из прочитанных сигналов
- Сигналы реальны и работают в проде: Solid, Angular 16+, Preact Signals, Vue ref/computed, Svelte 5 runes
- Предложение TC39 Signals на июнь 2026 года находится в комитете и пока не является частью стандарта JavaScript
- Выбор между VDOM и сигналами это выбор парадигмы, а не просто библиотеки: разные модели обновления UI
Связанные уроки
- sm-06-paradigms-overview — Обзор парадигм реактивности задаёт контекст: сигналы это одна из веток, противопоставленная подходу VDOM-перерисовки
- vue-27-pinia-intro — Pinia и реактивность Vue построены на ref и computed, которые по сути являются сигналами с другим именем
- ng-39-ngrx-signalstore — SignalStore в Angular строится поверх Angular signals и показывает сигналы внутри реального стор-решения