Vue
Под капотом: движок реактивности и будущее Vue
Изменение одного ref где-то в сторе обновляет ровно тот фрагмент интерфейса, который от него зависит, и не трогает остальное. Это выглядит как автоматизм, но за ним стоит конкретный механизм: движок во время рендера запоминает, какие реактивные значения прочитал каждый эффект, и строит граф зависимостей. Когда значение меняется, движок поднимает по графу ровно те эффекты, что его читали. Этот урок, завершающий курс, открывает капот: как устроен граф эффектов, что принёс алгоритм alien-signals в Vue 3.6, куда движется Vapor Mode и чем Vue отличается от React и Svelte по своей механике.
- Vue 3.6 (beta, 2026): движок реактивности переводится на алгоритм alien-signals для меньших накладных расходов
- Граф зависимостей эффектов: основа того, почему ре-рендерится только зависимое поддерево
- Vapor Mode: компиляция в прямые операции с DOM без виртуального DOM, экспериментальный режим
- Сигналы как общая идея: SolidJS, Svelte 5 runes, Angular signals и Vue сошлись на одной модели
- Сравнение подходов: Vue, React и Svelte решают одну задачу обновления UI разными механиками
Предварительные знания
- Глубокое понимание реактивности Vue: ref, reactive, computed, watchEffect
- Знание, что такое виртуальный DOM и render-функция
- Опыт с эффектами и отслеживанием зависимостей на уровне идеи
Граф эффектов и зависимостей
В основе реактивности Vue лежит понятие эффекта: функции, которую движок перезапускает, когда меняются данные, от которых она зависит. Render-функция компонента это тоже эффект. Ключевой трюк в том, что зависимости не объявляются вручную, а собираются автоматически во время выполнения. Когда эффект выполняется, движок помечает его как активный, и любое чтение реактивного значения внутри регистрирует это значение как зависимость эффекта.
Механизм держится на двух операциях. Track: при чтении реактивного свойства (через геттер прокси у reactive или через .value у ref) движок связывает текущий активный эффект с этим свойством, добавляя его в множество подписчиков. Trigger: при записи в свойство движок берёт это множество и перезапускает все подписанные эффекты. Так образуется двунаправленный граф: каждое значение знает своих подписчиков, каждый эффект знает свои зависимости.
computed это особый эффект: ленивый и кеширующий. Он не выполняется сразу при создании, а ждёт первого чтения. Результат кешируется и отдаётся при повторных чтениях без пересчёта. Computed подписывается на свои зависимости как обычный эффект, но при их изменении не пересчитывается немедленно, а лишь помечает себя устаревшим. Реальный пересчёт происходит при следующем чтении. Так цепочка computed-значений обновляется ровно один раз и только когда результат действительно запрошен.
Поскольку зависимости собираются на каждый запуск эффекта заново, граф всегда отражает только то, что эффект прочитал в этот раз. Если ветка кода с обращением к ref не выполнилась из-за условия, этот ref не попадёт в зависимости, и его изменение не перезапустит эффект. Это и есть точность мелкозернистой реактивности.
Как Vue узнаёт, какие именно реактивные значения являются зависимостями эффекта?
alien-signals и Vapor Mode
Базовый граф из множеств подписчиков работает, но имеет цену: каждая связь это запись в Set, каждое распространение изменения это обход коллекций, а память растёт с числом зависимостей. В Vue 3.6 (бета) движок реактивности переписывается на алгоритм alien-signals. Идея в более экономном представлении графа: связи между значениями и подписчиками хранятся как связанный список узлов, а распространение изменений идёт с отслеживанием версий, чтобы не пересчитывать то, что фактически не поменялось. Результат это меньший расход памяти и более быстрая работа на больших графах зависимостей при той же внешней модели API.
| Аспект | Классический граф | alien-signals (3.6) |
|---|---|---|
| Хранение связей | Множества подписчиков (Set) | Связанные узлы зависимостей |
| Распространение | Обход коллекций подписчиков | Версионирование, меньше лишних пересчётов |
| Память | Растёт с числом связей | Компактнее на крупных графах |
| API для разработчика | ref, reactive, computed | Те же, изменения внутренние |
Второе направление развития это Vapor Mode. Сегодня скомпилированная render-функция при выполнении строит дерево виртуальных узлов, которое движок сравнивает со старым и применяет разницу к DOM. Виртуальный DOM универсален, но его создание и дифф на каждый рендер стоят процессора и памяти. Vapor Mode меняет цель компиляции: компонент превращается не в render-функцию с виртуальными узлами, а в прямые операции с DOM, привязанные напрямую к реактивным значениям. Когда значение меняется, обновляется ровно тот узел DOM, что от него зависит, без построения и диффа промежуточного дерева.
Vapor Mode опирается на тот же реактивный движок: граф эффектов остаётся, меняется лишь то, что делает эффект при срабатывании. Вместо пересборки виртуального поддерева он выполняет точечную DOM-операцию. Это снижает накладные расходы рантайма и уменьшает размер бандла, потому что код виртуального DOM становится не нужен для vapor-компонентов. На июнь 2026 это режим, который вводится постепенно и сосуществует с обычным режимом в одном приложении.
Важно различать два слоя. alien-signals это про то, как устроен граф реактивности (как track и trigger работают эффективнее). Vapor Mode это про то, что эффект делает с DOM (прямая операция вместо виртуального узла). Они независимы и дополняют друг друга: первый ускоряет распространение изменений, второй удешевляет их применение к экрану.
Чем Vapor Mode отличается от классического режима рендеринга Vue?
Vue против React и Svelte
Все три фреймворка решают одну задачу: держать интерфейс синхронным с состоянием. Но механики у них разные, и понимание движка Vue помогает увидеть, где проходят границы. React при изменении состояния перезапускает компонент и всё его поддерево, строит новое дерево элементов и через reconciliation вычисляет разницу. Зависимости React не отслеживает автоматически: разработчик сам управляет ре-рендерами через memo, useMemo и useCallback, хотя React Compiler автоматизирует часть этого.
Vue идёт по пути мелкозернистой реактивности: граф эффектов знает, какой компонент зависит от какого значения, поэтому при изменении перерисовывается только зависимое поддерево, а не всё дерево от точки изменения вниз. Внутри компонента Vue по-прежнему использует виртуальный DOM с patch-флагами от компилятора, а Vapor Mode сдвигает это к прямым операциям с DOM. Svelte уходит дальше всех в компиляцию: реактивность превращается в прямые присваивания DOM на этапе сборки, и рантайм-фреймворка как такового почти нет.
- React — Ре-рендер от изменения вниз по дереву плюс reconciliation виртуального DOM. Зависимости вручную (memo, useMemo) или через React Compiler. Гибкость и огромная экосистема ценой ручного контроля обновлений
- Vue — Мелкозернистая реактивность с автоматическим графом зависимостей плюс виртуальный DOM с patch-флагами. Обновляется только зависимое поддерево. Vapor Mode движет к прямым операциям с DOM
- Svelte — Реактивность компилируется в прямые присваивания DOM на этапе сборки, почти без рантайма. Минимальный бандл ценой более тесной завязки на компилятор
| Критерий | React | Vue | Svelte |
|---|---|---|---|
| Отслеживание зависимостей | Ручное или компилятором | Автоматическое в рантайме | Компилятором при сборке |
| Единица ре-рендера | Компонент и поддерево | Зависимое поддерево | Конкретный DOM-узел |
| Виртуальный DOM | Да | Да, Vapor движет к отказу | Нет |
Заметна сходимость. Идея сигналов (мелкозернистая реактивность с автоматическими зависимостями) пришла в SolidJS, затем в Svelte 5 runes, в Angular signals, и движок Vue 3.6 на alien-signals тоже про это. Одновременно растёт роль компилятора: React Compiler, Vapor Mode и подход Svelte двигают работу со времени выполнения на время сборки. Вывод для разработчика трезвый: выбор фреймворка это компромисс между экосистемой, моделью реактивности и тем, сколько работы взять на компилятор, а не вопрос абсолютного превосходства одного над другими.
Сравнения по синтетическим бенчмаркам обманчивы: на реальных приложениях разница между фреймворками обычно меньше, чем разница между хорошо и плохо написанным кодом на любом из них. Понимание механики движка важнее цифр из бенчмарка, потому что именно оно позволяет писать код, который не борется с фреймворком.
Связь с другими темами
Капстоун-урок сводит воедино реактивность, сборку и направления развития Vue:
- Глубокая реактивность — Внутренний граф эффектов это механизм, который раньше изучался снаружи
- alien-signals — Новый алгоритм реактивности, на который перешло ядро Vue 3.6
- Vapor Mode — Будущая компиляция без виртуального DOM, опирающаяся на тот же реактивный движок
Итог
- Реактивность Vue это граф эффектов и зависимостей: во время выполнения эффект автоматически подписывается на все реактивные значения, которые прочитал
- Чтение реактивного значения регистрирует текущий активный эффект как подписчика (track), а запись уведомляет всех подписчиков и перезапускает их (trigger)
- computed это ленивый кешированный эффект: он пересчитывается только при чтении и только если изменилась хотя бы одна его зависимость
- alien-signals в Vue 3.6 переработал внутреннее устройство графа ради меньшего расхода памяти и более быстрого распространения изменений
- Vapor Mode компилирует компонент в прямые операции с DOM, убирая виртуальный DOM и его дифф, что снижает накладные расходы и стоимость гидрации
- Vue использует мелкозернистую реактивность плюс виртуальный DOM, React перерисовывает от изменения вниз с reconciliation, Svelte компилирует реактивность в прямые присваивания
Связанные уроки
- vue-23-alien-signals — alien-signals это новый алгоритм реактивности, который лёг в основу движка Vue 3.6
- vue-35-vapor-mode — Vapor Mode это направление компиляции без виртуального DOM, прямое продолжение темы движка
- vue-43-build-tooling — Компилятор SFC порождает render-функцию, которую исполняет реактивный движок из этого урока