Как React Fiber рендерит ваш интерфейс

#react#fiber#performance#virtual-dom

TL;DR

React Fiber — это переработанный движок согласования (reconciliation) в React, который реализует инкрементальный рендеринг через linked list of fibers. Он позволяет прерывать и возобновлять работу, приоритезировать задачи и эффективно обновлять DOM. Разберёмся, как это работает под капотом.

Введение: эволюция рендеринга в React

До Fiber React использовал синхронный стековый алгоритм reconciliation, который не мог быть прерван. Это вызывало проблемы с производительностью в complex UIs. Fiber архитектура (появилась в React 16) решает это через:

  1. Разбиение работы на chunks
  2. Приоритизацию обновлений (например, анимации > data fetching)
  3. Возможность паузы/возобновления работы
// До Fiber - recursive tree traversal
function reconcile(prev, next) {
  // ...рекурсивный обход всего дерева
}

Как устроен Fiber node

Каждый Fiber — это lightweight JavaScript object, представляющий единицу работы. Основные поля:

{
  tag: FunctionComponent | ClassComponent | HostComponent,
  key: null | string,
  elementType: string | Function,
  stateNode: DOMNode | Instance,
  return: Fiber | null,
  child: Fiber | null,
  sibling: Fiber | null,
  alternate: Fiber | null, // для diffing
  effectTag: Placement | Update | Deletion,
  nextEffect: Fiber | null // linked list изменений
}

Фактически, Fiber — это immutable work-in-progress tree, где каждый узел знает:

Фазы рендеринга

Fiber разделяет рендеринг на две фазы:

  1. Render phase (прерываемый):

    • Создание work-in-progress tree
    • Вызов render методов
    • Сравнение с предыдущей версией (reconciliation)
  2. Commit phase (синхронный):

    • Применение изменений к DOM
    • Вызов lifecycle методов
    • Запуск эффектов
// Упрощённый цикл работы Fiber
function workLoop(deadline) {
  while (nextUnitOfWork && deadline.timeRemaining() > 0) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }
  if (!nextUnitOfWork && workInProgressRoot) {
    commitRoot();
  }
  requestIdleCallback(workLoop);
}

Приоритизация задач

Fiber использует browser’s requestIdleCallback API (или полифиллы) для планирования работы:

const priorities = {
  Immediate: 1,       // Ввод пользователя
  UserBlocking: 2,    // Анимации
  Normal: 3,          // Обычные обновления
  Low: 4,             // Отложенные задачи
  Idle: 5             // Не критично
};

function scheduleUpdate(fiber, priority) {
  // ...логика добавления в очередь
}

Практические следствия

  1. Оптимизация рендеринга:
    • Используйте React.memo/PureComponent для предотвращения лишних ререндеров
    • Разбивайте тяжёлые компоненты на мелкие
const HeavyComponent = React.memo(({ items }) => (
  <div>
    {items.map(item => (
      <Item key={item.id} {...item} />
    ))}
  </div>
));
  1. Работа с Concurrent Mode:
    • Используйте Suspense для ленивой загрузки
    • Применяйте startTransition для не критичных обновлений
function App() {
  const [resource] = useState(() => createResource(fetchData()));
  
  return (
    <Suspense fallback={<Spinner />}>
      <Profile resource={resource} />
    </Suspense>
  );
}

Под капотом: diffing алгоритм

Fiber использует two-pass алгоритм сравнения:

  1. Первый проход (begin phase): создание work-in-progress tree
  2. Второй проход (complete phase): сбор эффектов
function performUnitOfWork(fiber) {
  // begin phase
  const children = reconcileChildren(fiber, fiber.props.children);
  
  if (fiber.child) return fiber.child;
  
  // complete phase
  let nextFiber = fiber;
  while (nextFiber) {
    completeWork(nextFiber);
    if (nextFiber.sibling) return nextFiber.sibling;
    nextFiber = nextFiber.return;
  }
}

Заключение

React Fiber — это эволюция core алгоритма React, которая:

Для глубокой оптимизации приложений понимание Fiber architecture критически важно. Используйте React DevTools Profiler для анализа производительности и помните: чем меньше Fiber nodes нужно обработать — тем быстрее ваш UI.


Источник: https://inside-react.vercel.app/blog/how-does-react-fiber-render-your-ui