dev-resources.site
for different kinds of informations.
Что делает React Compiler?
Реакт Компилятор (react compiler) - это инструмент для оптимизации React-приложений. Компилятор включается в работу во время сборки приложения и трансформирует код так, чтобы избежать ненужные ререндеры.
Компилятор анализирует код, составляет его упрощенное представление, находит места, которые можно оптимизировать, трансформирует выбранный код. Реакт Компилятор предполалагет, что код удовлетворяет Правилам Реакта - такой код не сломается от оптимизаций компилятора. Если код не удовлетворяет Правилам, то он будет проигнорирован.
Компилятор применяет к коду, как минимум, две трансформации: мемоизирует результат рендера и результат вычисления выражений внутри компонентов.
Мемоизация результата рендера
Результатом рендера я называю выражение, которое возвращается из компонента: return <div>{items}</div>
, в этом примере <div>{items}</div>
- результат рендера. Результат рендера зависит от пропсов и промежуточных значений, вычисленных внутри компонента. Назовем эти значения зависимостями выражения. Когда результат рендера остается постоянным при одних и тех же зависимостях, вычисление выражения можно пропустить. Для этого Реакт Компилятор вставит код, запоминающий последний результат вычисления:
- Если выражение вычисляется первый раз, то вычисляет выражение, сохраняет результат в кэш, сохраняет зависимости в кэш.
- Если выражение вычисляется повторно, сравнивает новые значения зависимостей с сохраненными в кэше:
- Если они равны, использует закэшированное значение выражения.
- Если они разные, вычисляет выражение с новыми зависимостями, сохраняет результат вычисления и зависимости в кэш.
Такой компонент:
function Div({ item, other }) {
return <div data-x={other}>{item}</div>;
}
Реакт Компилятор преобразует вот так:
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;
}
Можно догадаться, что в строке 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>;
}
В этом примере <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;
}
Реакт Компилятор закэширует 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>;
}
Включение Реакт Компилятора может не принести ощутимой пользы в проекте, в котором осознанно используются useMemo и memo.
Featured ones: