React Fiber под капотом: архитектура движка согласования, двойная буферизация и time slicing
Когда разработчик вызывает root.render(<App />) или обновляет состояние компонента через useState, библиотека React не перерисовывает интерфейс в браузере мгновенно. Под декларативным синтаксисом JSX скрывается сложный инженерный механизм — движок согласования (Reconciliation Engine). Именно он вычисляет разницу (diff) между текущим состоянием интерфейса и новым деревом компонентов, определяя минимальный набор изменений для применения к реальному DOM.
До версии React 16 этот процесс опирался на синхронный стековый алгоритм, что приводило к зависаниям интерфейса при тяжелых операциях. Переход на архитектуру React Fiber стал самым масштабным переписыванием ядра в истории библиотеки: он превратил рендеринг в прерываемый асинхронный процесс, заложив фундамент для конкурентного режима (Concurrent Mode) и современных возможностей React 18 и 19.
Проблема старого движка: почему рекурсивный Stack Reconciler блокировал браузер
Оригинальный механизм согласования React — Stack Reconciler — выполнял обход дерева компонентов рекурсивно, используя стандартный стек вызовов JavaScript (Call Stack). Когда приложение инициировало обновление через setState, движок рекурсивно обходил каждый компонент от корня до листовых узлов, сравнивал виртуальные деревья и не мог остановиться, пока стек вызовов полностью не опустеет.
Стандартом плавности в веб-разработке считается частота 60 кадров в секунду (60 FPS), что оставляет на кадр бюджет в 16,67 миллисекунды. С учетом внутренних задач браузера пользовательский код должен завершать вычисления примерно за 10 миллисекунд.
Если рекурсивный обход занимал 30 или 50 миллисекунд, главный поток выполнения JavaScript (Main Thread) блокировался. Браузер пропускал кадры, из-за чего пользователь сталкивался с рывками анимаций, зависанием прокрутки и задержкой реакции на ввод текста (эффект «заикания» интерфейса, или jank). Стековая модель не имела приоритизации: фоновое обновление большой таблицы блокировало ввод текста в поисковую строку, хотя реакция на ввод несравнимо важнее для пользователя.
Архитектура Fiber: виртуальный стек и односвязные списки узлов
Чтобы решить проблему блокировки потока, команда React разработала Fiber — структуру данных, представляющую собой виртуальный кадр стека (virtual stack frame), сохраняемый в оперативной памяти.
Вместо неизменяемых объектов React-элементов движок Fiber оперирует изменяемыми узлами (Fiber Nodes). Узел содержит состояние компонента, свойства (props) и ссылки на DOM-элементы. Главное новшество — отказ от рекурсивных вызовов в пользу односвязных списков:
child— указатель на первый дочерний элемент компонента.sibling— указатель на следующий соседний элемент на том же уровне.return— указатель на родительский узел (логический адрес возврата).alternate— указатель на парный узел в параллельном буферном дереве.memoizedPropsиmemoizedState— сохраненные свойства и состояние для проверки мемоизации и пропуска лишней работы.flags— битовая маска эффектов, фиксирующая необходимые мутации в DOM (вставка, обновление, удаление узла).
| Критерий | Stack Reconciler (React < 16) | Fiber Reconciler (React 16+) |
|---|---|---|
| Структура данных | Иммутабельное дерево объектов | Односвязные списки Fiber-узлов |
| Исполнение | Синхронная рекурсия в стеке JS | Итеративный цикл с прерываниями |
| Управление временем | До полного завершения дерева | Квантование времени (time slicing по 5 мс) |
| Приоритеты задач | Отсутствуют (FIFO) | Многоуровневые приоритеты (Lanes) |
| Влияние на UI | Дропы кадров при тяжелых обновлениях | Гарантированный отклик на действия пользователя |
Благодаря структуре связанных списков алгоритм обхода превращается из рекурсивной функции в обычный цикл while (workInProgress !== null), который можно приостановить в любой момент и возобновить позже с сохраненного узла.
Двухфазный жизненный цикл: Render vs Commit
Архитектура Fiber строго разделяет процесс обновления на две последовательные фазы с принципиально разным поведением:
- Фаза рендеринга (Render Phase): Является асинхронной и прерываемой. Движок строит черновик дерева в памяти (
workInProgress), вызывает функции компонентов и определяет список изменений. На каждом шаге планировщик проверяет оставшийся бюджет времени (shouldYield()). Если интервал в 5 миллисекунд исчерпан или поступило срочное событие (клик мыши), React отдает управление браузеру и возвращается к рендерингу в следующем макротаске черезMessageChannel. На этой фазе реальный DOM не модифицируется. - Фаза фиксации (Commit Phase): Является синхронной и непрерываемой. Когда черновик дерева полностью готов, React переносит все накопленные мутации в реальный DOM в один прием (
commitMutationEffects), предотвращая появление полусобранного интерфейса на экране. После этого запускаются методы жизненного цикла и хуки.
Двойная буферизация (Double Buffering) и планировщик Scheduler
Для плавных переходов без мерцания Fiber заимствует классический паттерн графических движков — двойную буферизацию. В оперативной памяти одновременно поддерживаются два дерева:
- Дерево
current: Отражает состояние интерфейса на экране. - Дерево
workInProgress: Рабочий черновик, асинхронно формируемый в фоновом режиме.
Каждый узел дерева current связан со своим зеркальным узлом в workInProgress через свойство alternate. Когда фаза фиксации завершается, React мгновенно переключает указатель корня приложения (FiberRoot.current) на готовое дерево workInProgress. Старое дерево не уничтожается сборщиком мусора, а переиспользуется как рабочий буфер для следующего цикла обновлений.
Планировщик (Scheduler) распределяет задачи по дорожкам приоритетов (Lanes):
- Срочные дискретные действия (Immediate / User-Blocking): клики, нажатия клавиш, ввод текста. Обрабатываются немедленно.
- Непрерывные взаимодействия (Continuous): перемещение курсора, скролл, перетаскивание элементов.
- Обычные обновления (Normal / Transition): выборка данных по сети, пагинация, переключение вкладок.
- Фоновые задачи (Idle): предварительная загрузка скрытых блоков, сбор аналитики.
Конкурентные возможности в React 18 и 19
Архитектура Fiber позволила внедрить в современные версии React революционные инструменты конкурентного рендеринга:
- Хук
useTransition: Позволяет явно пометить тяжелое обновление состояния как низкоприоритетный переход. Интерфейс мгновенно реагирует на ввод пользователя, пока тяжелый компонент рендерится в фоновом деревеworkInProgress. - Компонент
Suspenseи потоковый SSR: Позволяет декларативно задавать резервный интерфейс (fallback) на время асинхронной загрузки данных. На сервере Suspense стримит начальный HTML-каркас и досылает готовые блоки потоком, а в браузере выполняет селективную гидратацию (Selective Hydration) — оживляет интерактивностью в первую очередь те блоки, с которыми уже пытается взаимодействовать пользователь.
Практические правила для разработчиков: как писать Fiber-friendly код
Понимание внутренней механики Fiber помогает избегать скрытых багов и просадок производительности:
- Чистота функций рендеринга: Тела функциональных компонентов должны быть строго чистыми функциями. Поскольку фаза рендеринга может быть прервана, отменена или перезапущена движком несколько раз до коммита, выполнение побочных эффектов (мутация глобальных переменных, прямые манипуляции с DOM, запуск таймеров) внутри тела функции строго запрещено — их необходимо изолировать внутри хука
useEffect. - Предсказуемость структур и мемоизация: Чтобы Fiber мог пропустить обход поддерева узлов (механизм bailout), используйте
React.memo,useMemoиuseCallback. ЕслиpendingPropsузла идентичныmemoizedProps, движок мгновенно переиспользует старый Fiber-узел без запуска рендеринга дочерних элементов. - Стабильные ключи (
key): Всегда передавайте стабильные уникальные идентификаторы в списках, чтобы Fiber мог сопоставить узлы междуcurrentиworkInProgressбез полного пересоздания DOM-элементов.
Официальная архитектурная документация и исходный код движка доступны в репозитории React Reconciler Documentation. Понимание архитектуры Fiber позволяет проектировать отзывчивые масштабируемые веб-приложения, эффективно утилизирующие вычислительные ресурсы браузера.

![Node.JS [ru]](/api/digests/it_development/daily/20260827/assets/sources/we-use-js.jpg)