← Назад к вопросам

Как React рендерит страницу?

2.0 Middle🔥 211 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как React рендерит страницу

Рендеринг в React — это процесс преобразования JSX компонентов в HTML элементы, которые видит пользователь в браузере. Процесс состоит из нескольких этапов.

Этапы рендеринга

1. Парсинг JSX

Babel переводит JSX в вызовы React.createElement():

// JSX
<Component name="John" age={30} />

// Превращается в
React.createElement(Component, { name: 'John', age: 30 })

2. Создание Virtual DOM (VDOM)

React создаёт виртуальное представление дерева компонентов:

const vdom = React.createElement('div', null,
  React.createElement('h1', null, 'Заголовок'),
  React.createElement('p', null, 'Текст')
);

3. Reconciliation (сравнение)

React сравнивает новое VDOM со старым и находит различия:

// Старое VDOM
<div>
  <p>Старый текст</p>
</div>

// Новое VDOM
<div>
  <p>Новый текст</p>  // Изменился
  <span>Новый элемент</span>  // Добавился
</div>

// React вычисляет только изменения

4. Коммит (применение изменений)

React применяет изменения к реальному DOM:

// React обновляет:
// 1. Текст в <p>
// 2. Добавляет <span>
// 3. Оставляет <div> без изменений

Жизненный цикл рендеринга

function App() {
  // 1. RENDER PHASE (чистый, без побочных эффектов)
  const [count, setCount] = useState(0);
  
  // 2. Вычисление VDOM
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
  
  // 3. COMMIT PHASE (с побочными эффектами)
  // useEffect выполняется здесь
}

Fiber архитектура (React 16+)

React использует Fiber для разбиения рендеринга на небольшие куски:

// Раньше (React 15):
// Синхронный рендеринг — весь Stack за раз
// Проблема: браузер зависает при больших обновлениях

// Теперь (React 16+):
// Асинхронный рендеринг через Fiber
// Можно прервать и продолжить позже
// Приоритеты: пользовательское взаимодействие выше

Пример рендеринга

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log('Монтирование или обновление');
    return () => console.log('Очистка');
  }, [count]);
  
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Первый рендеринг:

  1. count = 0
  2. Возвращает: <div><h1>0</h1><button>+</button></div>
  3. Коммит в DOM
  4. useEffect выполняется

После клика:

  1. setCount(1) → плановый рендеринг
  2. count = 1
  3. Возвращает: <div><h1>1</h1><button>+</button></div>
  4. Сравнение VDOM → изменилась только строка "0" → "1"
  5. Обновление DOM (только текст)
  6. Cleanup из useEffect предыдущего
  7. useEffect нового рендеринга

Оптимизации рендеринга

1. React.memo — мемоизация компонентов

const Item = React.memo(({ name }) => {
  console.log('Рендер Item');
  return <li>{name}</li>;
});

function List() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <Item name="A" />
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  );
}
// Item не перерендерится при изменении count

2. useMemo — мемоизация значений

function Component() {
  const [count, setCount] = useState(0);
  
  const expensive = useMemo(() => {
    console.log('Вычисляю');
    return count * 2;
  }, [count]);
  
  return <div>{expensive}</div>;
}
// Вычисляет только когда count меняется

3. useCallback — мемоизация функций

function Parent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log(count);
  }, [count]);
  
  return <Child onClick={handleClick} />;
}
// handleClick пересоздаётся только при изменении count

Key в списках

// Без key — React переиспользует элементы
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>  // Плохо!
      ))}
    </ul>
  );
}

// С key — React отслеживает элементы правильно
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>  // Хорошо!
      ))}
    </ul>
  );
}

Батчинг обновлений (React 18+)

function handleClick() {
  // React группирует оба обновления в один рендеринг
  setCount(c => c + 1);
  setName('New');
  
  // Один VDOM сравнение
  // Один коммит
}

// Раньше нужно было оборачивать в flushSync для немедленного
import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => setCount(c => c + 1));
  // Немедленный рендеринг
}

Краткая схема

Компонент функция → Пропсы/State → VDOM
                                    ↓
                          Сравнение с предыдущим
                                    ↓
                          Найти различия (diff)
                                    ↓
                          Обновить реальный DOM
                                    ↓
                          Выполнить useEffect
                                    ↓
                          Пользователь видит

Производительность

// Профилирование в Chrome DevTools
// React DevTools → Profiler
// Видно какие компоненты рендерились и почему

// Избегай:
- Создания новых объектов в render
- Анонимных функций в пропсах
- Больших списков без key
- Глубоких вложенностей компонентов

Вывод

Рендеринг в React:

  1. Создаёт VDOM на основе пропсов/состояния
  2. Сравнивает с предыдущим VDOM
  3. Находит различия
  4. Обновляет реальный DOM
  5. Запускает побочные эффекты (useEffect)

Это очень эффективно благодаря Virtual DOM, Fiber архитектуре и оптимизациям.

Как React рендерит страницу? | PrepBro