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

Как будешь решать проблему производительности?

2.0 Middle🔥 231 комментариев
#Оптимизация и производительность

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Как будешь решать проблему производительности?

Методический подход

Производительность — это не гадание. Есть четкая методика: Measure -> Analyze -> Fix -> Verify.

Шаг 1: Measuring (Измерение)

Chrome DevTools Performance

// Открыть DevTools -> Performance -> Record
// Записать типичный user flow

// Или программно:
performance.mark('myFeature-start');
// ... код ...
performance.mark('myFeature-end');
performance.measure('myFeature', 'myFeature-start', 'myFeature-end');

const measure = performance.getEntriesByName('myFeature')[0];
console.log(`Заняло ${measure.duration}ms`);

Core Web Vitals

// Google Web Vitals library
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

getCLS(console.log); // Cumulative Layout Shift
getFID(console.log); // First Input Delay
getFCP(console.log); // First Contentful Paint
getLCP(console.log); // Largest Contentful Paint
getTTFB(console.log); // Time to First Byte

Lighthouse

# CLI запуск
lighthouse https://example.com --view

# Или в DevTools: Lighthouse tab

Network Tab

// Проверить:
// - Размер бандла
// - Кол-во запросов
// - Время загрузки каждого ресурса
// - Неиспользуемый код (Coverage tab)

Шаг 2: Analyzing (Анализ)

Найти "узкие места" (bottlenecks)

// 1. Медленный JavaScript
console.time('expensiveOperation');
processLargeArray(1000000);
console.timeEnd('expensiveOperation'); // 250ms

// 2. Лишние re-renders
function MyComponent() {
  const [count, setCount] = useState(0);
  const data = computeExpensiveData(); // переделывается при каждом render!
  return <div>{count}</div>;
}

// 3. Неоптимизированные изображения
// <img src="4MB.jpg" /> // слишком тяжелое

// 4. Забытые console.log и debugger
function handleClick() {
  console.log('debug'); // в продакшене!
  debugger; // или это!
  doSomething();
}

React Profiler

// DevTools -> Profiler -> Record -> Interact -> Analyze
// Видишь какие компоненты переделываются и сколько времени занимает

Шаг 3: Fixing (Исправление)

1. Code Splitting

// Не загружай всё сразу
const HeavyComponent = dynamic(
  () => import('./HeavyComponent'),
  { loading: () => <Spinner />, ssr: false }
);

// Маршруты
const AdminDashboard = lazy(() => import('./AdminDashboard'));
const UserProfile = lazy(() => import('./UserProfile'));

2. Memoization

// Избегай лишних расчетов
const MemoComponent = memo(function MyComponent({ prop }) {
  return <div>{prop}</div};
});

// Стабилизируй зависимости
const memoizedValue = useMemo(() => {
  return expensiveCalculation(a, b);
}, [a, b]);

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

3. Оптимизация изображений

// Next.js Image (автоматическое сжатие)
import Image from 'next/image';

<Image
  src="/large.jpg"
  alt="Description"
  width={800}
  height={600}
  priority={false} // lazy load
  quality={75} // сжимаем
/>

// WebP формат
<picture>
  <source srcSet="image.webp" type="image/webp" />
  <img src="image.jpg" alt="" />
</picture>

// Responsive images
<img
  src="small.jpg"
  srcSet="medium.jpg 768w, large.jpg 1200w"
  sizes="(max-width: 768px) 100vw, 50vw"
  alt=""
/>

4. Виртуализация списков

// Не рендери 10000 элементов, рендери видимые
import { FixedSizeList } from 'react-window';

function LargeList() {
  const Row = ({ index, style }) => (
    <div style={style}>Item {index}</div>
  );

  return (
    <FixedSizeList
      height={600}
      itemCount={10000}
      itemSize={35}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

5. Минимизация бандла

// Убери неиспользуемый код
// Chrome DevTools -> Coverage -> проверь какой код не используется

// Tree shaking в webpack/vite
// Экспортируй только нужное
export { usefulFunction }; // экспортируется
// uselessFunction не экспортируется -> будет удален

// Lazy load библиотеки
const lodash = () => import('lodash'); // только когда нужна

6. Кеширование

// React Query
const { data } = useQuery({
  queryKey: ['users'],
  queryFn: api.getUsers,
  staleTime: 5 * 60 * 1000, // 5 минут свежести
  cacheTime: 10 * 60 * 1000 // кешировать 10 минут
});

// Service Worker
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(r => r || fetch(e.request))
  );
});

7. Асинхронность

// Не блокируй main thread
setTimeout(() => { // отложи
  heavyCalculation();
}, 0);

// Используй requestIdleCallback
requestIdleCallback(() => {
  analyzeUserBehavior(); // когда браузер свободен
});

// Web Workers для тяжелых расчетов
const worker = new Worker('worker.js');
worker.postMessage({ data: largeData });
worker.onmessage = (e) => console.log(e.data);

Шаг 4: Verification (Проверка)

// Повторяем Шаг 1
performance.mark('after-fix');
// Сравниваем метрики ДО и ПОСЛЕ

// Ожидаемый результат:
// - LCP: было 5s, стало 2s
// - FCP: было 2s, стало 0.8s
// - Бандл: было 500KB, стало 200KB

Пример: Реальная оптимизация

// ДО: 5 сек загрузки
function ProductList() {
  const [products, setProducts] = useState([]);
  
  useEffect(() => {
    api.getAllProducts().then(setProducts); // загружаем всё
  }, []);
  
  return (
    <div>
      {products.map(p => (
        <ProductCard key={p.id} product={p} /> // рендерим всё
      ))}
    </div>
  );
}

// ПОСЛЕ: 1.5 сек загрузки
const HeavyProductCard = memo(ProductCard);

function ProductList() {
  const { data: products } = useQuery({
    queryKey: ['products'],
    queryFn: api.getProducts, // пагинация
    keepPreviousData: true
  });
  
  return (
    <FixedSizeList height={800} itemCount={products.length} itemSize={100}>
      {({ index, style }) => (
        <div style={style}>
          <HeavyProductCard product={products[index]} />
        </div>
      )}
    </FixedSizeList>
  );
}

Контрольный список оптимизации

  • Измерил Core Web Vitals
  • Запустил Lighthouse
  • Проверил размер бандла
  • Удалил мертвый код
  • Оптимизировал изображения
  • Добавил code splitting
  • Убрал лишние re-renders (useMemo, useCallback)
  • Внедрил виртуализацию для больших списков
  • Настроил кеширование
  • Проверил Network tab (запросы, водопад)
  • Повторно измерил метрики
  • Зафиксировал результат

Заключение

Оптимизация производительности — это систематический процесс, не случайное гаданье. Всегда:

  1. Меряй текущее состояние
  2. Анализируй узкие места
  3. Исправляй по приоритету
  4. Проверяй результаты

Маленкие оптимизации складываются в большой результат: быстрое приложение = больше пользователей = выше конверсия.