Теперь это ваши проблемы
Вы когда-нибудь задумывались, как ваше приложение справляется со всеми своими задачами? Почему некоторые действия подтормаживают или страницы загружаются медленно? Тут как раз и начинается наша детективная работа — нужно выяснить, что происходит "под капотом". Профилирование позволяет не только понять, какие компоненты тратят слишком много ресурсов, но и оптимизировать их работу.
Цели профилирования
- Найти узкие места, которые замедляют работу приложения.
- Определить компоненты, которые рендерятся слишком часто без необходимости.
- Проанализировать длительные операции (например, сложные вычисления).
- Улучшить пользовательский опыт (UX) за счёт более отзывчивого интерфейса.
Если вас когда-нибудь раздражал тормозящий сайт, то поздравляю, теперь это ваши проблемы. И лично вам придется их решить :P
Использование React DevTools для профилирования
Для начала давайте установим React DevTools, если вы этого ещё не сделали. Это ваш основной инструмент профилирования. С его помощью можно увидеть, как работают компоненты, сколько времени тратится на их рендер и где именно нужно копать глубже.
Установка React DevTools
- Установите React DevTools как расширение для браузера Chrome или Firefox.
- Если вы работаете с локальным проектом, убедитесь, что он использует React версии >= 16.5 (именно с этой версии профилирование стало доступным).
После установки вы увидите новую вкладку в инструментах разработчика браузера — React. Переключитесь на неё и откройте интерфейс профилирования Profiler.
Запуск профилирования
Давайте разберём, как провести анализ производительности нашего React-приложения. Вот пример приложения, которое вы уже могли создавать в предыдущих лекциях:
import React, { useState, useEffect } from 'react';
type Todo = {
id: number;
title: string;
completed: boolean;
};
const TodoItem: React.FC<{ todo: Todo }> = React.memo(({ todo }) => {
console.log(`Rendering TodoItem: ${todo.id}`);
return (
<div>
<input type="checkbox" checked={todo.completed} readOnly />
<span>{todo.title}</span>
</div>
);
});
const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then((response) => response.json())
.then(setTodos);
}, []);
return (
<div>
<h1>Todo List</h1>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</div>
);
};
export default App;
Это простое приложение с TodoItem, где мы загружаем и отображаем список дел. Однако, предположим, что оно подтормаживает. Как узнать, в чём проблема?
Запуск профайлера
- Откройте ваше приложение в браузере.
- Перейдите на вкладку
Reactв DevTools. - Нажмите на кнопку
Profilerи начните записьStart Profiling. - Выполните действие в приложении (например, пролистайте список).
- Остановите запись
Stop Profilingи изучите результаты.
Интерпретация данных профилирования
После остановки профилирования вы увидите граф, который показывает, какие компоненты рендерились, сколько времени это заняло и что именно вызвало рендер.
Что искать?
- Долгие рендеры — компоненты, которые занимают наибольшую часть времени в общей схеме.
- Частые рендеры — компоненты, которые вызываются снова и снова без очевидной причины.
- Перерисовки без изменений — ситуации, когда компонент рендерится, хотя его пропсы/состояние не изменились.
🚨 Совет: если видите компонент, который рендерится слишком часто, это хороший кандидат для оптимизации с React.memo.
Пример оптимизации
В представленном выше приложении, если вы добавите лог в TodoItem, каждый вызов рендера этого компонента будет отображаться в консоли. Но на самом деле нам не нужно перерисовывать каждый компонент TodoItem, если меняется, например, только заголовок списка.
Чтобы исправить это, мы оборачиваем TodoItem в React.memo. Это мы уже сделали, чтобы предотвратить лишние рендеры. Но бывают случаи, когда объект todo сам по себе создаётся заново при каждом рендере App, из-за чего мемоизация не срабатывает. В таких случаях можно использовать useCallback или useMemo для предотвращения создания новых объектов.
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
Проблема здесь в том, что каждый раз при рендере массив todos остаётся неизменным, но React всё равно может считать элементы вложенного компонента новыми. Исправим это.
Советы и лучшие практики при профилировании
Находите главного виновника: не все медленные рендеры являются явной проблемой. Ищите основного виновника — компонент, который запускает цепную реакцию.
Не оптимизируйте всё подряд: иногда затраты на оптимизацию больше, чем её польза. Профилируйте только проблемные области.
Используйте
React.memoи хуки с умом: излишнее применение может привести к усложнению кода.Типизируйте всё чётко: это не только помогает избежать ошибок, но и улучшает читаемость и поддержку кода.
Тестируйте на различных устройствах: особенно важно для приложений, рассчитанных на мобильные устройства.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ