← Назад к вопросам
Как происходит отрисовка элементов на странице в React?
1.7 Middle🔥 143 комментариев
#React#Архитектура и паттерны
Комментарии (3)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Процесс отрисовки элементов на странице в React
Отрисовка (rendering) - это процесс преобразования компонентов в DOM элементы, видимые пользователю. React использует Virtual DOM для оптимизации этого процесса.
Этапы отрисовки
1.렌더Rendering фазе
// При изменении state/props React запускает render
function Counter() {
const [count, setCount] = useState(0);
// Функция компонента вызывается - это render
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
// React.createElement вывов:
React.createElement('div', null,
React.createElement('p', null, 'Count: ', count),
React.createElement('button', {...}, 'Increment')
);
2. Reconciliation (сравнение)
// Virtual DOM - это объект, не настоящий DOM
const vdom = {
type: 'div',
props: { className: 'container' },
children: [
{ type: 'p', props: {}, children: ['Hello'] },
{ type: 'button', props: { onClick: handleClick }, children: ['Click'] }
]
};
// React сравнивает старый и новый vdom
function reconcile(oldVdom, newVdom) {
// Если тип изменился - пересоздать
if (oldVdom?.type !== newVdom.type) {
return 'replace';
}
// Если текст изменился - обновить
if (oldVdom?.children !== newVdom.children) {
return 'update';
}
return 'same';
}
3. Commit фаза (реальное обновление DOM)
// После reconciliation React обновляет реальный DOM
// 1. Обновляет элементы
document.querySelector('p').textContent = 'New text';
// 2. Запускает effects
useEffect(() => {
console.log('Component mounted or updated');
}, [dependency]);
// 3. Запускает cleanup функции
useEffect(() => {
return () => {
console.log('Cleanup');
};
}, []);
Полный цикл жизни компонента
function LifecycleExample() {
const [count, setCount] = useState(0);
// 1. RENDER фаза - функция компонента вызывается
console.log('Render');
// 2. При монтировании
useEffect(() => {
console.log('Mount');
// 3. COMMIT фаза - DOM обновлен
// 4. При размонтировании
return () => {
console.log('Unmount');
};
}, []); // зависимости пусты - один раз
// При изменении count
useEffect(() => {
console.log('Count changed to', count);
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
// Порядок при клике:
// 1. onClick вызывается
// 2. setCount обновляет state
// 3. Render фаза - функция вызывается заново
// 4. Reconciliation - Virtual DOM сравнивается
// 5. Commit фаза - DOM обновляется
// 6. useEffect с [count] зависимостью запускается
React Fiber - детальный процесс
// React разбивает работу на куски (fibers)
function renderWithFiber(element) {
// 1. Создать fiber дерево
let fiber = {
type: element.type,
props: element.props,
dom: null,
parent: null,
child: null,
sibling: null
};
// 2. Пройти по всему дереву
reconcileChildren(fiber, element.props.children);
// 3. Создать/обновить DOM в commit фазе
commitRoot();
}
// Это позволяет React:
// - Делить работу на части
// - Прерывать и продолжать рендер
// - Приоритизировать обновления
Оптимизация отрисовки
Избегай ненужных re-renders
// ПЛОХО - Parent перерисовывается часто
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<Child data={count} /> {/* Child перерисуется */}
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
);
}
// ХОРОШО - memo предотвращает ненужный render
const Child = React.memo(({ data }) => {
return <div>{data}</div>;
});
// ХОРОШО - разделить state
function Parent() {
return (
<div>
<ChildWithOwnState />
<Counter />
</div>
);
}
useMemo и useCallback
function ExpensiveComponent({ items, onFilter }) {
// Кэшируем результат, если items не изменились
const filteredItems = useMemo(() => {
return items.filter(item => item.active);
}, [items]);
// Кэшируем функцию, чтобы Child не перерисовывалась
const handleClick = useCallback((id) => {
onFilter(id);
}, [onFilter]);
return (
<div>
{filteredItems.map(item => (
<Item key={item.id} onClick={() => handleClick(item.id)} />
))}
</div>
);
}
Порядок выполнения hooks
function HookOrder() {
// 1. State инициализируется
const [count, setCount] = useState(0);
// 2. Effects выполняются ПОСЛЕ render
useEffect(() => {
console.log('After render:', count);
// Cleanup выполнится перед следующим render
return () => {
console.log('Before next render or unmount');
};
}, [count]);
// 3. Компонент возвращает JSX
return <div>{count}</div>;
}
// При монтировании:
// State инициализируется -> JSX возвращается -> Effect выполняется
// При update:
// Cleanup выполняется -> State обновляется -> JSX возвращается -> Effect
Батчинг (Batching)
function BatchingExample() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const handleClick = () => {
// React 18 батчит эти вызовы
// Только один render и один commit!
setA(a + 1);
setB(b + 1);
};
// Даже в async коде в React 18
const handleAsync = async () => {
await fetchSomething();
// Батчится автоматически!
setA(a + 1);
setB(b + 1);
};
return <button onClick={handleClick}>Click</button>;
}
Отрисовка в React - это не просто обновление DOM, это сложный процесс оптимизации, обхода, и управления побочными эффектами. Профессиональный разработчик должен понимать каждый этап.