Как работает алгоритм Fiber (объяснение своими словами)
React → Reconciliation → Fiber
Основная идея
Fiber — это алгоритм reconciliation в React, который позволяет разбивать рендеринг на небольшие порции работы и распределять их по нескольким кадрам. Это делает интерфейс отзывчивым даже при сложных обновлениях.
Ключевые аспекты
Инкрементальный рендеринг — работа делится на маленькие части (units of work)
Приоритезация — срочные обновления (анимации, клики) выполняются раньше
Прерываемость — React может приостановить работу и вернуться к ней позже
Две фазы — render (можно прервать) и commit (нельзя прервать)
Linked list — Fiber-ноды связаны через child, sibling, return
Как это работает
React создаёт Fiber-дерево из компонентов
При обновлении обходит дерево порциями
Между порциями проверяет, нужно ли уступить главному потоку
Накапливает изменения в памяти
Применяет все изменения к DOM за один раз (commit)
Частые ошибки на собеседованиях
Путают Fiber (архитектура) с Virtual DOM (концепция) — Fiber это реализация работы с Virtual DOM
Думают, что Fiber делает React многопоточным — React всё ещё однопоточный
Не понимают разницу между render и commit фазами
Забывают, что прерываемость работает только в Concurrent Mode
Введение и проблематика
Проблема старого алгоритма Stack Reconciler
До React 16 использовался Stack Reconciler — алгоритм, который обрабатывал обновления рекурсивно и синхронно. Это создавало серьёзную проблему: если дерево компонентов было большим, браузер "зависал" на время обработки.
js
// Представьте дерево из 10 000 компонентов// Stack Reconciler обрабатывал бы их все за один проход// Браузер не мог бы отрисовать анимацию или обработать клик
Главный поток браузера отвечает и за JavaScript, и за отрисовку UI. Если JS занят больше 16мс, пользователь видит "лаги".
Зачем нужен Fiber
Fiber решает эту проблему, разбивая работу на маленькие порции. React может:
Приостановить работу и вернуться к ней позже
Назначать приоритеты разным типам обновлений
Переиспользовать ранее выполненную работу
Отменять работу, если она больше не нужна
Базовая теория
Что такое Fiber-нода
Fiber-нода — это JavaScript-объект, который представляет единицу работы. Для каждого React-элемента создаётся соответствующая Fiber-нода.
❓
Code Example 1: Что представляет собой Fiber-нода? Для чего нужны свойства child, sibling, return?
ts
// Упрощённая структура Fiber-нодыinterfaceFiber {// Тип компонента type:any; key:string|null;// Связи с другими нодами (linked list) child:Fiber|null; // первый ребёнок sibling:Fiber|null; // следующий брат return:Fiber|null; // родитель// Состояние pendingProps:any; // новые props memoizedProps:any; // props после рендера memoizedState:any; // state после рендера// Эффекты flags:Flags; // какие операции нужны (Placement, Update, Deletion)// Для работы планировщика lanes:Lanes; // приоритет обновления}
Linked List вместо стека
В отличие от рекурсии, связный список позволяет "запомнить" где остановились:
graphTD A[App]--> B[Header] A --> C[Main] A --> D[Footer] C --> E[Sidebar] C --> F[Content] style A fill:#e1f5fe style C fill:#fff3e0
Обход происходит так:
Спуск к первому ребёнку (child)
Если нет детей — переход к брату (sibling)
Если нет братьев — возврат к родителю (return)
Две фазы работы
Render Phase (Reconciliation)
Можно прервать и возобновить
Выполняется "в памяти"
Вычисляет, какие изменения нужны
Вызывает render(), shouldComponentUpdate()
❓
Code Example 2: Как работает функция performUnitOfWork? Почему используется связный список вместо рекурсии?
js
// React обходит дерево и вычисляет различияfunctionperformUnitOfWork(fiber) {// 1. Выполнить работу для этой нодыbeginWork(fiber);// 2. Если есть ребёнок — вернуть его как следующую работуif (fiber.child) {returnfiber.child; }// 3. Иначе — завершить работу и перейти к брату/родителюcompleteUnitOfWork(fiber);}
Практический пример работы алгоритма
Шаг 1: Создание Fiber-дерева
При первом рендере React создаёт Fiber-дерево из JSX:
❓
Code Example 4: Какое Fiber-дерево создаст React для этого компонента?
Когда вызывается setState, React планирует обновление:
jsx
const [count,setCount] =useState(0);// setCount(1) создаёт "update" объект и планирует работу
Шаг 3: Work Loop
Планировщик запускает цикл работы:
❓
Code Example 5: Как workLoop позволяет браузеру обрабатывать другие задачи? Что такое time slicing?
js
functionworkLoop(deadline) {// Пока есть работа и времяwhile (nextUnitOfWork &&deadline.timeRemaining() >0) { nextUnitOfWork =performUnitOfWork(nextUnitOfWork); }// Если время вышло — уступаем браузеруif (nextUnitOfWork) {requestIdleCallback(workLoop); }}
Шаг 4: Commit
После завершения render phase, все изменения применяются за один раз.
Приоритеты обновлений
React разделяет обновления по приоритетам:
Приоритет
Пример
Поведение
Immediate
Клик, ввод текста
Выполняется сразу
User-blocking
Hover эффекты
Высокий приоритет
Normal
Загрузка данных
Стандартный приоритет
Low
Аналитика
Может откладываться
Idle
Предзагрузка
Выполняется в свободное время
⚠️
Приоритезация полностью работает только в Concurrent Mode (React 18+). В Legacy Mode все обновления синхронные.
Double Buffering
React использует технику "двойной буферизации":
text
Current Tree Work-in-Progress Tree(то, что на экране) (новая версия) ↓ ↓ [Fiber] ←──alternate──→ [Fiber] ↓ ↓ [Fiber] ←──alternate──→ [Fiber]
Current — дерево, которое сейчас отображается
Work-in-Progress — дерево, которое строится
После commit они меняются местами
Пограничные кейсы
⚠️
Важно: Render phase может выполняться несколько раз для одного обновления! Не делайте side effects в render.
❓
Code Example 6: Почему side effects в render — это проблема в Concurrent Mode? Как исправить этот код?
jsx
// ❌ Неправильно — side effect в renderfunctionComponent() {console.log('render'); // может вызваться несколько разsendAnalytics(); // опасно!return <div>...</div>;}// ✅ Правильно — side effects в useEffectfunctionComponent() {useEffect(() => {sendAnalytics(); // вызовется один раз после commit }, []);return <div>...</div>;}
Плюсы и минусы
Аспект
Stack Reconciler
Fiber
Отзывчивость
❌ Блокирует UI
✅ Прерываемый
Приоритеты
❌ Нет
✅ Есть
Сложность
✅ Простой
❌ Сложный
Память
✅ Меньше
❌ Больше (два дерева)
Concurrent features
❌ Нет
✅ Suspense, Transitions
Вопросы интервьюера
Q: Почему React может вызвать render несколько раз?
В Concurrent Mode React может прервать render phase и начать заново, если пришло более приоритетное обновление. Поэтому render должен быть чистой функцией.
Q: Что такое "time slicing"?
Техника разбиения работы на кусочки по ~5мс, чтобы не блокировать главный поток. Между кусочками браузер может обрабатывать события и рисовать.
Q: Как Fiber связан с Suspense?
Fiber позволяет "приостановить" рендеринг компонента, ожидая данные. Без прерываемости Suspense был бы невозможен.
Q: Все ли обновления прерываемые?
Нет. flushSync() делает обновление синхронным. Также в Legacy Mode все обновления синхронные.