DEV Community

Cover image for Что делает React Compiler?
jennypollard
jennypollard

Posted on

1

Что делает React Compiler?

Реакт Компилятор (react compiler) - это инструмент для оптимизации React-приложений. Компилятор включается в работу во время сборки приложения и трансформирует код так, чтобы избежать ненужные ререндеры.

Компилятор анализирует код, составляет его упрощенное представление, находит места, которые можно оптимизировать, трансформирует выбранный код. Реакт Компилятор предполалагет, что код удовлетворяет Правилам Реакта - такой код не сломается от оптимизаций компилятора. Если код не удовлетворяет Правилам, то он будет проигнорирован.

Компилятор применяет к коду, как минимум, две трансформации: мемоизирует результат рендера и результат вычисления выражений внутри компонентов.

Мемоизация результата рендера

Результатом рендера я называю выражение, которое возвращается из компонента: return <div>{items}</div>, в этом примере <div>{items}</div> - результат рендера. Результат рендера зависит от пропсов и промежуточных значений, вычисленных внутри компонента. Назовем эти значения зависимостями выражения. Когда результат рендера остается постоянным при одних и тех же зависимостях, вычисление выражения можно пропустить. Для этого Реакт Компилятор вставит код, запоминающий последний результат вычисления:

  1. Если выражение вычисляется первый раз, то вычисляет выражение, сохраняет результат в кэш, сохраняет зависимости в кэш.
  2. Если выражение вычисляется повторно, сравнивает новые значения зависимостей с сохраненными в кэше:
  3. Если они равны, использует закэшированное значение выражения.
  4. Если они разные, вычисляет выражение с новыми зависимостями, сохраняет результат вычисления и зависимости в кэш.

Такой компонент:

function Div({ item, other }) {
  return <div data-x={other}>{item}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Реакт Компилятор преобразует вот так:

import { c as _c } from "react/compiler-runtime";
function Div(t0) {
  const $ = _c(3);
  const { item, other } = t0;
  let t1;
  if ($[0] !== item || $[1] !== other) {
    t1 = <div data-x={other}>{item}</div>;
    $[0] = item;
    $[1] = other;
    $[2] = t1;
  } else {
    t1 = $[2];
  }
  return t1;
}
Enter fullscreen mode Exit fullscreen mode

Можно догадаться, что в строке const $ = _c(3) Реакт Компилятор создает локальный кэш на 3 элемента. В действительности, c из react/compiler-runtime вызывает внутри себя React.useMemo.

В случае, когда компонент использует хуки или контекст, сгенерированный код выглядет немного сложнее, но идея таже: сравнить текущие зависимости компонента с закэшированными, если они поменялись закэшировать новые зависимости, вычислить новое значение, закэшировать новое значение.

Код в примере выше аналогичен оборачиванию компонента в memo.

Мемоизация выражений

В примере выше, jsx-выражение зависило только от пропсов компонента. Часто в компонентах вычисляются промежуточные значения, которые подставляются в jsx-выражения. Эти промежуточные значения также могут быть мемоизированы. Для этого Реакт Компилятор определит зависимости выражения и вставит код для кэширования всего выражения на основе этих зависимостей.

Рассмотрим пример, в котором внутри компонента происходит вычисление:

export default function App(props) {

  const name = props.fullname.split(' ')[0];

  return <main>
    {name}
  </main>;
}
Enter fullscreen mode Exit fullscreen mode

В этом примере <main>{name}</main> - это jsx-выражение, его зависимость - переменная name, которая в свою очередь вычисляется выше выражением props.fullname.split(' ')[0]. Выражение props.fullname.split(' ')[0] зависит только от пропсы fullname.
Реакт Компилятор преобразует этот компонент следующим образом:

import { c as _c } from "react/compiler-runtime";
export default function App(props) {
  const $ = _c(4);
  let t0;
  if ($[0] !== props.fullname) {
    t0 = props.fullname.split(" ");
    $[0] = props.fullname;
    $[1] = t0;
  } else {
    t0 = $[1];
  }
  const name = t0[0];
  let t1;
  if ($[2] !== name) {
    t1 = <main>{name}</main>;
    $[2] = name;
    $[3] = t1;
  } else {
    t1 = $[3];
  }
  return t1;
}
Enter fullscreen mode Exit fullscreen mode

Реакт Компилятор закэширует props.fullname и результат вычисления name. Если пропса fullname поменяется, выражение будет вычислено и закэшировано еще раз. Этот код аналогичен оборачиванию вычисления name в useMemo в исходном компоненте:

import {useMemo} from 'react';

export default function App(props) {

  const name = useMemo(() => props.fullname.split(' ')[0], [props.fullname]);

  return <main>
    {name}
  </main>;
}
Enter fullscreen mode Exit fullscreen mode

Включение Реакт Компилятора может не принести ощутимой пользы в проекте, в котором осознанно используются useMemo и memo.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Cloudinary image

Video API: manage, encode, and optimize for any device, channel or network condition. Deliver branded video experiences in minutes and get deep engagement insights.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay