awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Объект fiber и дерево с двойной буферизацией

Суть Fiber — простой JS-объект, один на компонент, хранящий состояние, хуки и указатели дерева. Связно-списочная структура делает рендеринг прерываемым, а два дерева — двойная буферизация — делают прерывание безопасным.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 12 min

React не может приостановить рекурсивную функцию — стек вызовов принадлежит движку, а не React. Fiber-архитектура решила это, заменив стек на структуру данных, которой React управляет сам: связное дерево объектов, в котором можно остановиться и продолжить с любого узла.

Что такое fiber на самом деле. Fiber — обычный JavaScript-объект, один на каждый экземпляр компонента, один на каждый DOM-элемент в дереве. Каждый fiber хранит: type компонента, его props, его key, текущее state и хуки, указатели на child, следующий sibling и return (родитель), плюс указатель alternate на двойника в другом дереве. Файберы образуют связное дерево, а не массив — и именно связность является ключевым свойством.

Рекурсивный обход дерева живёт в стеке вызовов и не может быть приостановлен; обход связного списка хранит свою позицию в обычной переменной, так что React может остановиться после любого fiber, сохранить указатель и продолжить с точно того же места позже. Fiber — структура данных, сделавшая прерываемый рендеринг возможным. Название появилось до React 18 — архитектура появилась в React 16 (2017) — но только React 18 включил конкурентные функции, эксплуатирующие её.

Поля объекта fiber
ПолеЧто хранит
typeФункция компонента или тег DOM
propsТекущие props, переданные этому узлу
keyКлюч реконсиляции
memoizedStateСвязный список записей хуков (useState, useEffect …)
child / sibling / returnУказатели навигации по дереву (не стек вызовов)
alternateУказатель на двойника в другом дереве

Два дерева: current и workInProgress. React всегда держит два fiber-дерева. Дерево current — то, чьё состояние совпадает с тем, что сейчас нарисовано на экране. Когда начинается обновление, React строит дерево workInProgress — клонируя файберы из current по мере прохода, применяя новые props и state. Указатель alternate каждого fiber связывает его с двойником в другом дереве, поэтому клонирование дёшево: неизменившиеся поддеревья переиспользуются по ссылке.

Когда дерево workInProgress завершено и закоммичено, два дерева меняются местами: workInProgress становится current, а старое current хранится, чтобы стать следующим workInProgress. Это двойная буферизация. Она гарантирует, что дерево на экране никогда не наблюдается в полуобновлённом состоянии — и именно поэтому рендер, прерванный и выброшенный, ничего не стоит: выброшенное дерево workInProgress просто никогда не коммитится.

Почему это работает

Почему fiber, а не рекурсия? До React 16 реконсиляция была обычной рекурсивной функцией. Рекурсия живёт в стеке вызовов JS-движка, который нельзя приостановить и возобновить — функция либо выполняется до конца, либо не выполняется вовсе. Это означало, что весь рендер большого дерева был одной неделимой синхронной задачей. Fiber заменил рекурсию явным обходом связного списка: «стек вызовов» стал структурой данных в куче, которую React владеет и может остановить, сохранить и продолжить. Название отсылает к концепции «fiber» из системного программирования — пользовательского облегчённого потока, — потому что fiber-дерево по сути является вручную реализованным прерываемым стеком вызовов поверх обычного JS.

Stack-реконсилер: чем fiber лучше предшественника. До React 16 реконсилер был написан как стек — один большой синхронный обход. Команда React называла его «stack reconciler». Его фатальный изъян: рендер большого приложения мог занимать 60–100 мс, и всё это время главный поток был заблокирован. Нажатия клавиш, анимации, скролл — всё ждало завершения React. Fiber-реконсилер сохраняет прогресс в явном heap-объекте, что позволяет прерваться после любого fiber и уступить браузеру. Это структурное изменение, а не оптимизация: рекурсию нельзя «ускорить» до прерываемости — нужна другая структура данных.

Викторина

Почему реконсиляция со связным fiber-деревом прерываема, а рекурсивный подход — нет?

Викторина

Рендер наполовину завершён, когда приходит более приоритетное обновление. React выбрасывает незавершённое дерево workInProgress и начинает заново. Почему это ничего не стоит навсегда?

Расставь шаги по порядку

Расставьте шаги одного цикла рендеринга React по порядку — от изменения состояния до смены деревьев.

  1. 1 Приходит обновление — React планирует работу на соответствующем lane
  2. 2 Начинается обход fiber — клонирование файберов из current в workInProgress
  3. 3 Запуск функций компонентов, реконсиляция нового дерева элементов
  4. 4 Commit: применение мутаций DOM из workInProgress
  5. 5 Swap: workInProgress становится current; старый current хранится для следующего цикла
Вспомните перед уходом
  1. 01
    Какие поля объекта fiber обеспечивают обход связного дерева?
  2. 02
    На что указывает поле alternate у fiber?
  3. 03
    Почему выброшенное дерево workInProgress можно выбросить с нулевыми видимыми последствиями?
Итог

Fiber — обычный JS-объект, хранящий type, props, key, state и хуки компонента, плюс указатели child/sibling/return, образующие связное дерево. В отличие от рекурсивного стека вызовов, это связное дерево можно остановить после любого узла, сохранив cursor-переменную, — что и является структурной основой прерываемого рендеринга. React всегда держит два таких дерева: current (совпадающее с экраном) и workInProgress (строящееся обновление). Указатель alternate каждого fiber связывает его с двойником в другом дереве, делая клонирование дешёвым. При коммите деревья меняются местами; при отмене рендера дерево workInProgress выбрасывается с нулевой стоимостью — оно никогда не трогало DOM.

Связанные уроки
встречается в143
Продолжить восхождение ↑Чистота фазы render и подшаги фазы commit
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.