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 на этапе сборки, почти без рантайма. Минимальный бандл ценой более тесной завязки на компилятор
КритерийReactVueSvelte
Отслеживание зависимостейРучное или компиляторомАвтоматическое в рантаймеКомпилятором при сборке
Единица ре-рендераКомпонент и поддеревоЗависимое поддеревоКонкретный 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-функцию, которую исполняет реактивный движок из этого урока
Под капотом: движок реактивности и будущее Vue

0

1

Войти

В чём ключевое отличие модели обновления Vue от модели React?