React Fiber: Погружение в оптимизацию рендеринга
surf_webПривет, я Артур, Surf Frontend Developer. Сегодня расскажу про важные концепции и структуры данных, связанные с алгоритмом Fiber, и объясню, как React это использует. Поговорим про асинхронное выполнение задач и рассмотрим пример с компонентом, который рендерит большой список элементов.
Теоретическая база
Для начала немного теории. React — это JavaScript-библиотека для создания пользовательских интерфейсов. Основной механизм React отслеживает изменения состояния компонентов и обновляет их на экране. Этот процесс называется согласованием (reconciliation). С версии 16 React представил новую реализацию внутреннего дерева экземпляров и алгоритм, названный Fiber.
У старого синхронного подхода React было несколько проблем. Все изменения выполнялись сразу, что могло блокировать основной поток и тормозить интерфейс. Это особенно проявлялось в крупных приложениях с множеством взаимодействующих компонентов.
К тому же традиционные методы не давали гибкости в управлении приоритетами. Из-за этого высокоприоритетные задачи, такие как ввод данных или анимации, часто выполнялись с задержкой, что мешало обеспечивать плавность и отзывчивость интерфейсов.
Эти проблемы подтолкнули разработчиков к созданию React Fiber — новой архитектуры, которая позволяет делать рендеринг асинхронным. Он разделяет задачи на маленькие части и эффективно распределяет ресурсы.
Fiber: Новый взгляд на управление рендерингом
Каждый React-элемент преобразуется в узел Fiber, который хранит информацию о состоянии компонентов и DOM. В отличие от обычных React-элементов, узлы Fiber обновляются, а не создаются заново при каждом рендере.
Архитектура Fiber позволяет эффективно отслеживать, планировать и управлять выполнением задач, что значительно улучшает производительность и отзывчивость интерфейса.
Что такое узлы Fiber и как они работают
Fiber-структура представляет собой не дерево, а связный список (Linked List). Узлы в нем соединены через указатели child, sibling и return, что формирует иерархию компонентов. Эта структура помогает React отслеживать связи между узлами и эффективно организовывать выполнение задач.
После первого рендера React создаёт текущее дерево Fiber. При последующих обновлениях создаётся рабочее дерево (workInProgress), которое отражает изменения, которые должны быть внесены. Это дерево обрабатывается и заменяет текущее (current), обеспечивая целостность интерфейса — React обновляет весь UI целиком, избегая частичных изменений.
Основные этапы работы: Render и Commit
Fiber позволяет React разделить работу на два ключевых этапа:
Render (Фаза рендеринга)
На этом этапе React определяет, какие изменения нужно внести для обновления UI. Render-фаза асинхронна, и её выполнение может быть приостановлено в любой момент, чтобы не блокировать главный поток.
Commit (Фаза коммита)
После фазы рендеринга React синхронно применяет изменения к DOM. Это обеспечивает мгновенное обновление интерфейса, гарантируя, что все изменения будут видны пользователю без задержек.
Пример оптимизации с использованием React Fiber
Одним из ключевых преимуществ Fiber является возможность асинхронного выполнения задач, что позволяет React лучше управлять производительностью приложения. Рассмотрим пример с компонентом, который рендерит большой список элементов.
В компоненте каждое изменение текста вызывает перерендеринг компонента SlowList, который, в свою очередь, генерирует 250 дочерних элементов. Этот процесс искусственно замедляется для демонстрации: каждый элемент блокирует поток на 1 мс, что приводит к фризам интерфейса при вводе текста.
const SlowItem = ({ text }: IListItem) => {
let startTime = performance.now();
while (performance.now() - startTime < 1) {}
return <li className="item">Text: {text}</li>;
};
Оптимизация с useTransition
Хук useTransition позволяет пометить обновления как низкоприоритетные, откладывая их выполнение, чтобы более важные задачи, такие как ввод текста, выполнялись без задержек. Однако при использовании useTransition инпут становится неуправляемым, так как ввод не сразу отображается в value поля.
export const OptimizedWithTransition = () => {
const [text, setText] = useState("");
const [isPending, startTransition] = useTransition();
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
startTransition(() => setText(value));
};
return (
<div>
<input onChange={(e) => setText(e.target.value)} />
<SlowList text={deferredText} />
</div>
);
};
Чтобы сохранить управляемый ввод, можно воспользоваться useDeferredValue, который задерживает только низкоприоритетные обновления, оставляя значение поля инпута актуальным.
Оптимизация с useDeferredValue
export const OptimizedWithDeferredValue = () => {
const [text, setText] = useState("");
const deferredText = useDeferredValue(text);
return (
<div>
<input onChange={(e) => setText(e.target.value)} />
<SlowList text={deferredText} />
</div>
);
};
С useDeferredValue React откладывает обновление значений, используемых в ресурсозатратных компонентах, сохраняя плавность работы поля ввода. В этом случае SlowList рендерится с небольшой задержкой, но поле остаётся управляемым, так как текущее значение текста обновляется мгновенно.
Преимущества Fiber для современных интерфейсов
Внедрение Fiber дало React ряд существенных улучшений:
- Плавность работы интерфейса — асинхронный рендеринг исключает «зависания», делая интерфейсы более отзывчивыми.
- Приоритизация задач — важные действия, такие как обработка ввода, обрабатываются раньше, чем менее приоритетные.
- Поддержка Concurrent Mode — Fiber стал основой для Concurrent Mode, который позволяет одновременно обрабатывать несколько версий UI.
Подведём итоги
Благодаря асинхронной архитектуре Fiber React эффективно управляет рендерингом, распределяя задачи по приоритету. Хуки useTransition и useDeferredValue помогают улучшить производительность приложения, предотвращая фризы интерфейса. Первый подходит для низкоприоритетных обновлений, но не всегда работает с управляемыми компонентами. В таких случаях оптимальным решением будет useDeferredValue, который сохраняет мгновенный отклик на ввод и плавно обновляет ресурсоёмкие компоненты.
Теперь, когда вы познакомились с Fiber и хуками для оптимизации, самое время применить их в своём проекте. Делитесь результатами и задавайте свои вопросы в комментариях.
Полезные ссылки
- Codesandbox — Примеры из поста
- Inside Fiber
- What is React Fiber and How It Helps You Build High-Performing React Applications
- useDeferredValue
- useTransition
Ещё больше полезной информации в нашем Telegram-канале