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

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

React Profiler, компилятор и продакшн-наблюдаемость

Суть React DevTools Profiler показывает, почему каждый fiber перерендерился и сколько это заняло. React Compiler автоматизирует мемоизацию. LoAF-записи связывают продакшн-жалобы на лаги с конкретными React-коммитами.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Пользователи сообщают о лагах. В офисе всё работает нормально. Profiler показывает commit на 38 мс, вызванный нажатием клавиши в поле поиска — и 240 ProductCard перерендериваются с причиной «parent rendered, props unchanged». Это сигнатура бага. Без Profiler вы гадаете; с ним исправление механично.

Чтение React Profiler. React DevTools Profiler записывает timeline по commit-ам. Для каждого commit он показывает, какие файберы рендерились, почему каждый рендерился («props changed», «hook changed», «parent rendered», «context changed») и сколько каждый занял. Senior-воркфлоу: запись взаимодействия, поиск слишком длинного commit-а, сортировка файберов по self-time, чтение причины «why did this render».

  • «Parent rendered» при неизменившихся props → отсутствующий bailout. Обернуть в React.memo, стабилизировать ссылку на нарушающий prop.
  • «Context changed» на множестве компонентов → нестабильное значение контекста. Мемоизировать значение провайдера через useMemo.
  • «Hook changed» → изменилось значение useState или useReducer. Проверить, мемоизировано ли вычисление, производящее его.
Найди ошибку
log
Commit #47  —  render: 38.2 мс  (1 из 1 закоммичено)

<App>                отрендерился  — 0.4 мс  — "hook changed"
  <Header>           отрендерился  — 0.2 мс  — "parent rendered"
  <Sidebar>          отрендерился  — 0.3 мс  — "parent rendered"
  <ProductGrid>      отрендерился  — 36.9 мс — "parent rendered"
    <ProductCard> x240  отрендерился  — 0.15 мс каждый — "parent rendered"

Почему отрендерился <App>?  -> useState (searchTerm) изменился
Примечание: props у <Header>, <Sidebar>, <ProductGrid> не изменились

Нажатие клавиши в поле поиска вызывает commit на 38 мс. Header, Sidebar и все 240 ProductCard рендерятся с причиной 'parent rendered', и Profiler говорит, что их props не изменились. Как исправить?

Продакшн-наблюдаемость: LoAF + Profiler. Profiler даёт локальное воспроизведение; Long Animation Frames (LoAF) дают продакшн-атрибуцию. Когда LoAF-запись атрибутирует медленный кадр React-бандлу, Profiler воспроизводит это локально и называет точные файберы. Вместе они закрывают петлю от «пользователи сообщают о лагах» до «этот компонент рендерится 200× из-за инлайнового style-объекта на строке 44».

Воркфлоу Profiler → исправление

1. Запись взаимодействия в Profiler

2. Поиск самого длинного commit — сортировка по общему времени рендера

3. Чтение причины «why did this render» для медленных файберов

4. Исправление: «parent rendered» + стабильные props → React.memo; «context changed» → мемоизировать значение провайдера; state вниз → коллокация состояния

5. Проверка — исправленный commit должен показывать только реально изменившиеся файберы

React Compiler и будущее мемоизации. Десятилетие поддержание bailout означало ручной разброс useMemo, useCallback и React.memo — утомительно, подвержено ошибкам, и само по себе не бесплатно (каждая memo-ячейка стоит памяти и сравнения). React Compiler (бывший «React Forget», стабильный трек с React 19) — build-time инструмент, анализирующий код компонентов и вставляющий мемоизацию автоматически и корректно, с более тонкой гранулярностью, чем делает человек. Он понимает, какие значения зависят от каких входных данных, и мемоизирует именно их, так что bailout срабатывает без рукописных хуков.

Senior-вывод: механизм — ссылочная стабильность, открывающая bailout — никуда не денется; это фундамент, на котором строится компилятор. Понимание того, почему стабильная ссылка позволяет fiber пропустить работу, — это то, что позволяет читать вывод компилятора, отлаживать его при консервативных bailout-ах и рассуждать о производительности вне зависимости от того, написана мемоизация вручную или сгенерирована компилятором.

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

Почему виртуальный DOM — не просто «накладные расходы». «Виртуальный DOM» — маркетинговое название для дерева элементов, которое диффит React. Стоит быть точным: виртуальный DOM не быстрее прямой манипуляции DOM — рукописная, оптимальная последовательность DOM-вызовов всегда победит React. Что покупает виртуальный DOM — это модель программирования: вы описываете UI как чистую функцию состояния и никогда не пишете императивный DOM-код, а React делает результат достаточно быстрым через дифф. Стоимость реальна — построение и дифф дерева элементов — это CPU-работа, которой избегает компилируемый фреймворк (Svelte) или fine-grained реактивный (SolidJS). Ставка React: выигрыш DX от «UI = f(state)» стоит накладных расходов реконсиляции, а time-slicing не даёт этим накладным расходам никогда блокировать пользователя.

Выбери лучший вариант

Поле поиска фильтрует таблицу из 5000 строк. Набор текста тормозит. Выберите основное исправление.

Спроектируй

Спроектируйте стратегию рендеринга для real-time торгового дашборда: грид цен из 2000 строк, обновляющийся ~10× в секунду, плюс интерактивные фильтры и панель деталей. Необходимо держать INP ниже 200 мс и никогда не жертвовать отзывчивостью поля ввода.

  • Тики цен приходят ~10 раз в секунду через WebSocket.
  • Правильными должны быть только видимые строки; грид прокручивается.
  • Элементы управления фильтром и сортировкой должны ощущаться мгновенными.
  • Фаза commit должна оставаться короткой — никаких длинных непрерываемых блоков.
  • Внешний store цен не должен рваться (tearing) при конкурентных рендерах.
  • Цель INP: ниже 200 мс на p75.
Викторина

React Compiler вставляет мемоизацию автоматически. Важно ли по-прежнему понимать useMemo и useCallback?

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

Вспомните перед уходом
  1. 01
    Трейс Profiler показывает 240 ProductCard, перерендеривающихся с причиной 'parent rendered, props unchanged'. Что это говорит и что делать?
  2. 02
    Что такое Long Animation Frame (LoAF) и как он помогает диагностировать проблемы производительности React?
  3. 03
    Какой ключевой инсайт о React Compiler должен держать в голове senior-инженер?
Итог

React DevTools Profiler записывает каждый commit с причинами рендера и длительностью на fiber. «Parent rendered, props unchanged» — механическая сигнатура отсутствующего bailout: добавьте React.memo и стабилизируйте нестабильные ссылки prop через useMemo/useCallback. «Context changed» на множестве компонентов указывает на нестабильное значение контекста. В продакшне Long Animation Frames (LoAF) атрибутируют медленные кадры React-коду — связывайте их с Profiler, чтобы назвать точный commit и файберы. React Compiler (стабильный трек в React 19) вставляет мемоизацию автоматически во время сборки, устраняя ручной разброс useMemo/useCallback. Но underlying-механизм — ссылочная стабильность, открывающая bailout — остаётся: компилятор строится на нём, и нужно понимать его для отладки консервативных решений компилятора и рассуждений о производительности в любой React-кодовой базе.

Связанные уроки
встречается в143
Продолжить восхождение ↑React fiber: тест с выбором ответа
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.