Алгоритм Fiber — это сердце архитектуры React (начиная с 16 версии). Если говорить просто, это полная переработка механизма рендеринга, которая превратила процесс обновления интерфейса из «непрерывного потока» в «умный планировщик».
Вот как это устроено «под капотом».
До Fiber React использовал Stack Reconciler. Он работал синхронно: если у вас было огромное дерево компонентов, React начинал его обновлять и не останавливался, пока не закончит.
- Проблема: Если обновление занимало 200 мс, браузер «зависал». Пользователь не мог кликнуть на кнопку или увидеть анимацию, потому что главный поток (Main Thread) был занят.
Fiber ввел инкрементальный рендеринг: возможность разбивать работу на части, приостанавливать её и возвращаться к ней позже.
Fiber — это обычный JavaScript-объект, который соответствует компоненту. В отличие от старого подхода, где React просто вызывал функции компонентов, теперь он создает дерево «файберов».
Каждый Fiber-узел содержит:
- State и Props: данные компонента.
- Тип: (div, Button, и т.д.).
- Ссылки: на родителя (
return), первого ребенка (child) и соседний элемент (sibling). - Очередь обновлений: что именно нужно изменить.
Алгоритм делит работу на два ключевых этапа:
На этом этапе React строит новое дерево компонентов и определяет, что изменилось.
- Асинхронность: Этот этап можно прервать. Если пришло высокоприоритетное событие (например, ввод текста пользователем), React поставит текущую сборку дерева на паузу, обработает ввод, а затем продолжит или начнет заново.
- Результат: Формируется список изменений (эффектов).
Когда работа по сравнению закончена, React переходит к применению изменений в реальный DOM.
- Синхронность: Этот этап нельзя прерывать. Он должен пройти быстро, чтобы пользователь увидел результат мгновенно и без визуальных артефактов.
Fiber использует планировщик (Scheduler), который назначает задачам приоритеты:
- Immediate: (Синхронно) — например, ввод текста.
- User Blocking: — клики, анимации.
- Normal: — сетевые запросы, обработка данных.
- Low/Idle: — логирование, аналитика.
Пример: Если React занят отрисовкой тяжелого списка (Normal), но пользователь нажимает на кнопку (User Blocking), React «бросает» список, обрабатывает клик и только потом возвращается к списку.
React Fiber использует технику из графики: у него всегда есть два дерева.
- Current Tree: То, что сейчас отображено на экране.
- Work-in-progress Tree: То, что React собирает в памяти прямо сейчас.
Как только "черновик" (work-in-progress) готов, React просто меняет указатель, и это дерево становится текущим. Это позволяет избежать ситуации, когда пользователь видит «наполовину обновленный» интерфейс.
Резюме: Fiber превратил React из "библиотеки для рендеринга" в эффективную "операционную систему для UI", которая умеет управлять временем и приоритетами.
Fiber-дерево — это не DOM-дерево. Более того, в современной архитектуре React понятие "Virtual DOM" стало более размытым, так как Fiber взял на себя его основные функции, но сделал их гораздо мощнее.
Давайте разложим всё по полочкам и найдем место Virtual DOM в этой цепочке.
Чтобы не запутаться, представьте три уровня структуры:
- React Elements: Это те самые объекты, которые возвращает
JSX. Они очень легкие и "одноразовые". Каждый раз при рендеринге они создаются заново. - Fiber Tree (и есть современный Virtual DOM): Это долгоживущие объекты. Они не пересоздаются при каждом чихе, а обновляются. Именно здесь хранится состояние (state) и хуки.
- Real DOM: Конечные узлы в браузере (
<div>,<span>).
Раньше (до Fiber) Virtual DOM был просто набором объектов, которые React сравнивал рекурсивно.
Сейчас Fiber-дерево — это и есть реализация Virtual DOM. Когда говорят "Virtual DOM" в контексте React 16+, обычно подразумевают процесс работы с Fiber-узлами.
Ключевое отличие:
- Старый VDOM: Был просто "снимком" (snapshot) интерфейса. Сравнение шло по принципу «вот старый снимок, вот новый, найди 10 отличий».
- Fiber: Это не просто снимок, а инфраструктура. Каждый Fiber-узел — это единица работы (unit of work). Он знает не только как выглядит элемент, но и когда его нужно обновить, какой у него приоритет и где находится его состояние.
Процесс обновления выглядит так:
- Событие: Вы вызываете
setState. - Планирование: React помечает Fiber-узел как требующий обновления и добавляет работу в очередь.
- Reconciliation (Сравнение): React идет по дереву Fiber-узлов. Он создает "черновое" дерево (work-in-progress), сравнивая новые React Elements (из JSX) со старыми Fiber-узлами.
- Diffing: Именно здесь происходит классический алгоритм сравнения (Diffing), который мы привыкли называть "магией Virtual DOM".
- Commit: Когда Fiber-дерево полностью готово и размечено (кому удалиться, кому добавиться), React одним махом переносит эти изменения в Real DOM.
Вы могли бы спросить: «Зачем нам Fiber, если можно сразу менять DOM?».
Проблема в том, что DOM очень медленный и "неуклюжий".
- В DOM нельзя "поставить на паузу" создание узла.
- В DOM нельзя легко рассчитать изменения в фоновом потоке.
Fiber-дерево выступает как "умная прослойка". Это песочница, в которой React может спокойно ошибаться, передумывать, сортировать задачи по важности и перестраивать структуру, не беспокоя браузер и не вызывая тормозов интерфейса.
Virtual DOM — это концепция. Fiber — это конкретная реализация этой концепции, которая превратила "простое сравнение объектов" в сложную систему управления задачами.
Внутри React есть центральный цикл, который называется Work Loop.
В упрощенном виде он выглядит примерно так:
while (nextUnitOfWork !== null && !shouldYield()) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}nextUnitOfWork: Это текущий Fiber-узел, который обрабатывает React.shouldYield(): Это «предохранитель». React постоянно спрашивает у браузера: «У меня еще есть время в текущем кадре (16.6мс) или я уже начинаю тормозить интерфейс?». Если времени нет, цикл прерывается, React отдает управление браузеру (чтобы тот отрисовал анимации или обработал клики), а затем возвращается к работе.
React обходит дерево не рекурсивно (как раньше), а итеративно, используя связи child, sibling и return. Это превращает дерево в подобие связного списка, по которому можно гулять вперед-назад, останавливаясь в любой точке.
Здесь важно разделять состояние дерева в памяти и реальные узлы в браузере.
Когда работа над «черновиком» (Work-in-progress tree) закончена, происходит следующее:
React действительно просто переключает одну ссылку в корне приложения (hostRoot), чтобы current дерево теперь указывало на новое, только что собранное дерево.
- Зачем: Это происходит мгновенно. Теперь React считает новое дерево «истинным» источником данных и состояния.
Но само по себе переключение ссылки в JavaScript-объектах не заставит браузер перерисовать кнопку или изменить текст. Real DOM — это внешнее API.
Во время этапа Reconciliation, обходя Fiber-узлы, React помечает их специальными флагами (эффектами):
Placement(нужно создать и вставить узел)Update(нужно изменить атрибуты существующего узла)Deletion(нужно удалить)
В фазе Commit React берет список этих эффектов и линейно (быстрым циклом) выполняет мутации в реальном DOM:
document.appendChild(), node.textContent = ..., node.setAttribute().
Важно: Эти два шага происходят в рамках одной фазы Commit. Сначала подготавливается DOM (мутации), а затем переключается ссылка на дерево, чтобы методы жизненного цикла (или
useEffect) видели уже обновленную картину.
В браузере нет концепции «теневого DOM-дерева», которое можно мгновенно подменить одним движением (кроме технических нюансов типа DocumentFragment, но они не решают проблему глобально).
DOM — это живая структура. Если вы хотите изменить <h1> на <h2>, вам в любом случае придется вызвать команду браузеру на удаление одного и создание другого.
Итого:
- Loop существует — он обрабатывает Fiber-узлы по одному, пока есть свободное время.
- Ссылка переключается, но только внутри React (между "текущим" и "завершенным" Fiber-деревом).
- Real DOM обновляется вручную через стандартные браузерные методы на основе меток, расставленных в Fiber-дереве.
Хотите узнать, как React понимает, что нужно прервать цикл (тот самый shouldYield), и как он использует MessageChannel для этого?