Przejdź do głównej zawartości

Optymalizacja wydajności - Cursor

Twój dashboard analityki mediów społecznościowych ma problemy. Aplikacja ładuje się 8 sekund, operacje wyszukiwania kończą się timeoutem, dashboard zamarza podczas aktualizacji danych w czasie rzeczywistym, a użycie pamięci rośnie do 2GB po godzinie. Z 100,000 codziennych aktywnych użytkowników narzekających, musisz osiągnąć czasy ładowania poniżej sekundy i 60fps interakcje.

Po ukończeniu tej lekcji opanujesz:

  • Profilowanie wydajności wspomagane AI
  • Systematyczne identyfikowanie wąskich gardeł
  • Optymalizację wydajności renderowania React
  • Optymalizację zapytań do bazy danych
  • Wykrywanie i naprawianie wycieków pamięci
  • Budowanie systemów monitorowania wydajności
  • Zrozumienie podstaw wydajności web
  • Doświadczenie z Chrome DevTools
  • Ukończone poprzednie lekcje
  • Podstawowa wiedza o narzędziach profilowania

Przekształć wolną aplikację przez:

  • Zmniejszenie początkowego ładowania z 8s do poniżej 2s
  • Osiągnięcie 60fps interakcji UI
  • Obcięcie użycia pamięci o 75%
  • Optymalizację zapytań bazy danych do poniżej 50ms
  • Efektywną implementację aktualizacji w czasie rzeczywistym
  • Budowanie monitorowania wydajności
  1. Początkowa analiza wydajności

Zacznij od kompleksowego profilowania:

"Przeanalizuj ten profil wydajności Chrome i raport Lighthouse:
- Zidentyfikuj top 5 wąskich gardeł wydajności
- Kategoryzuj problemy (renderowanie, scripting, sieć)
- Sugeruj kolejność priorytetów dla napraw
- Oszacuj wpływ każdej optymalizacji
@performance-profile.json @lighthouse-report.json"
  1. Stwórz baseline wydajności
"Stwórz zestaw testów wydajności, który mierzy:
- Time to Interactive (TTI)
- First Contentful Paint (FCP)
- Cumulative Layout Shift (CLS)
- Użycie pamięci w czasie
- Czasy odpowiedzi API
- Czasy renderowania komponentów React
Skonfiguruj automatyczne śledzenie wydajności"

Przykładowy skrypt baseline:

performance/baseline.ts
import { chromium } from 'playwright';
import lighthouse from 'lighthouse';
export async function measurePerformance(url: string) {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// Włącz śledzenie wydajności
await context.addInitScript(() => {
window.performanceMarks = {};
// Nadpisz profiler React
if (window.React && window.React.Profiler) {
const originalProfiler = window.React.Profiler;
window.React.Profiler = function(props) {
const onRender = (id, phase, actualDuration) => {
window.performanceMarks[id] = {
phase,
duration: actualDuration,
timestamp: performance.now()
};
props.onRender?.(id, phase, actualDuration);
};
return originalProfiler({ ...props, onRender });
};
}
});
// Mierz metryki strony
const startTime = Date.now();
await page.goto(url, { waitUntil: 'networkidle' });
const metrics = await page.evaluate(() => {
const navigation = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
return {
// Timing nawigacji
ttfb: navigation.responseStart - navigation.requestStart,
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
// Timing malowania
fcp: paint.find(p => p.name === 'first-contentful-paint')?.startTime,
lcp: performance.getEntriesByType('largest-contentful-paint')[0]?.startTime,
// Profilowanie React
componentRenders: window.performanceMarks,
// Pamięć
memory: performance.memory ? {
usedJSHeapSize: performance.memory.usedJSHeapSize,
totalJSHeapSize: performance.memory.totalJSHeapSize,
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit
} : null
};
});
await browser.close();
return metrics;
}
  1. Profiluj zapytania bazy danych

Przełącz się na tryb Agent:

@src/api
"Dodaj profilowanie zapytań do wszystkich operacji bazy danych:
- Loguj czas wykonania zapytań
- Identyfikuj zapytania N+1
- Znajdź brakujące indeksy
- Wykryj wolne zapytania (>100ms)
- Śledź częstotliwość zapytań
Stwórz dashboard wydajności zapytań"
  1. Optymalizacja renderowania React
@src/components/Dashboard.tsx
"Zoptymalizuj ten komponent React pod kątem wydajności:
- Dodaj właściwe memoization
- Implementuj wirtualizację dla list
- Użyj React.lazy dla code splitting
- Zoptymalizuj re-rendery z useMemo/useCallback
- Napraw anti-wzorce wydajnościowe
Pokaż porównanie przed/po"

Przykład optymalizacji:

// Przed: Wolny dashboard z niepotrzebnymi re-renderami
const Dashboard = () => {
const [data, setData] = useState([]);
const [filter, setFilter] = useState('');
// Źle: Tworzy nową tablicę przy każdym renderze
const filteredData = data.filter(item =>
item.name.includes(filter)
);
// Źle: Inline funkcja powoduje re-rendery dzieci
return (
<div>
{filteredData.map(item => (
<ExpensiveComponent
key={item.id}
item={item}
onClick={() => handleClick(item.id)}
/>
))}
</div>
);
};
// Po: Zoptymalizowane z memoization i wirtualizacją
const Dashboard = () => {
const [data, setData] = useState([]);
const [filter, setFilter] = useState('');
// Dobrze: Zmemoizowane filtrowane dane
const filteredData = useMemo(() =>
data.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
),
[data, filter]
);
// Dobrze: Stabilna referencja callback
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
// Dobrze: Wirtualizowana lista dla wydajności
const rowRenderer = useCallback(({ index, style }) => {
const item = filteredData[index];
return (
<div style={style}>
<MemoizedExpensiveComponent
item={item}
onClick={handleClick}
/>
</div>
);
}, [filteredData, handleClick]);
return (
<AutoSizer>
{({ height, width }) => (
<List
height={height}
width={width}
rowCount={filteredData.length}
rowHeight={80}
rowRenderer={rowRenderer}
overscanRowCount={5}
/>
)}
</AutoSizer>
);
};
// Zmemoizowany komponent dziecko
const MemoizedExpensiveComponent = memo(ExpensiveComponent,
(prevProps, nextProps) => {
return prevProps.item.id === nextProps.item.id &&
prevProps.item.updated === nextProps.item.updated;
}
);
  1. Optymalizacja rozmiaru bundle
"Przeanalizuj i zoptymalizuj bundle JavaScript:
- Zidentyfikuj duże zależności
- Sugeruj możliwości tree-shaking
- Implementuj strategię code splitting
- Lazy load ciężkie komponenty
- Zoptymalizuj konfigurację webpack
Cel: Zmniejsz bundle o 60%"
  1. Optymalizacja obrazów i zasobów
@public/assets
"Zoptymalizuj wszystkie obrazy i zasoby:
- Konwertuj obrazy do WebP
- Implementuj responsywne obrazy
- Dodaj lazy loading
- Użyj CDN dla zasobów statycznych
- Implementuj cache service workera"
  1. Optymalizacja odpowiedzi API
@src/api/routes
"Zoptymalizuj endpointy API pod kątem wydajności:
- Dodaj cache odpowiedzi z Redis
- Implementuj paginację prawidłowo
- Używaj projekcji bazy danych
- Dodaj filtrowanie pól
- Kompresuj odpowiedzi
Każdy endpoint powinien odpowiadać w poniżej 100ms"

Przykład zoptymalizowanego endpointu:

api/posts/optimized.ts
import { Redis } from 'ioredis';
import { compress } from 'zlib';
import { promisify } from 'util';
const redis = new Redis();
const gzipAsync = promisify(compress);
export async function getOptimizedPosts(req: Request, res: Response) {
const {
page = 1,
limit = 20,
fields = ['id', 'title', 'summary', 'author', 'createdAt'],
sort = '-createdAt'
} = req.query;
// Sprawdź cache najpierw
const cacheKey = `posts:${page}:${limit}:${fields.join(',')}:${sort}`;
const cached = await redis.get(cacheKey);
if (cached) {
res.setHeader('X-Cache', 'HIT');
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Encoding', 'gzip');
return res.send(Buffer.from(cached, 'base64'));
}
// Zoptymalizowane zapytanie do bazy danych
const posts = await db.post.findMany({
select: fields.reduce((acc, field) => {
acc[field] = true;
return acc;
}, {}),
skip: (page - 1) * limit,
take: limit,
orderBy: parseSort(sort),
// Użyj filtrowania na poziomie bazy danych
where: {
status: 'published',
deletedAt: null
}
});
// Dołącz metadane paginacji
const total = await db.post.count({
where: { status: 'published', deletedAt: null }
});
const response = {
data: posts,
meta: {
page,
limit,
total,
pages: Math.ceil(total / limit)
}
};
// Kompresuj odpowiedź
const json = JSON.stringify(response);
const compressed = await gzipAsync(json);
// Cache na 5 minut
await redis.setex(cacheKey, 300, compressed.toString('base64'));
res.setHeader('X-Cache', 'MISS');
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Encoding', 'gzip');
res.send(compressed);
}
  1. Optymalizacja zapytań bazy danych
@src/queries
"Zoptymalizuj te wolne zapytania bazy danych:
- Dodaj odpowiednie indeksy
- Przepisz nieefektywne zapytania
- Implementuj cache wyników zapytań
- Użyj materialized views gdzie stosowne
- Dodaj plany wykonania zapytań"
  1. Optymalizacja aktualizacji w czasie rzeczywistym
"Zoptymalizuj WebSocket/aktualizacje w czasie rzeczywistym:
- Implementuj inteligentne throttling
- Użyj aktualizacji różnicowych
- Dodaj kolejkowanie wiadomości
- Implementuj obsługę backpressure
- Zoptymalizuj format serializacji"
  1. Wykrywanie wycieków pamięci
"Przeanalizuj ten snapshot heap i znajdź wycieki pamięci:
- Zidentyfikuj zatrzymane obiekty
- Znajdź wycieki event listenerów
- Wykryj problemy pamięci closure
- Zlokalizuj wycieki węzłów DOM
- Śledź rosnące tablice/mapy
@heap-snapshot.heapsnapshot"

Przykład naprawy wycieku pamięci:

// Przed: Wyciek pamięci z event listenerów
class DataManager {
constructor() {
this.data = [];
this.handlers = new Map();
}
subscribe(event, handler) {
// Wyciek: Nigdy nie usuwa handlerów
if (!this.handlers.has(event)) {
this.handlers.set(event, []);
}
this.handlers.get(event).push(handler);
window.addEventListener('resize', () => {
this.recalculate();
});
}
recalculate() {
// Wyciek: Ciągle rośnie
this.data.push(new Array(1000000));
}
}
// Po: Właściwe czyszczenie i zarządzanie pamięcią
class DataManager {
constructor() {
this.data = [];
this.handlers = new Map();
this.resizeHandler = this.recalculate.bind(this);
this.maxDataSize = 100;
}
subscribe(event, handler) {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event).add(handler);
// Dodaj listener tylko raz
if (this.handlers.get(event).size === 1) {
window.addEventListener('resize', this.resizeHandler);
}
// Zwróć funkcję unsubscribe
return () => {
const handlers = this.handlers.get(event);
handlers.delete(handler);
if (handlers.size === 0) {
window.removeEventListener('resize', this.resizeHandler);
this.handlers.delete(event);
}
};
}
recalculate() {
// Implementuj circular buffer
if (this.data.length >= this.maxDataSize) {
this.data.shift();
}
this.data.push(this.calculateNewData());
}
destroy() {
// Wyczyść wszystko
window.removeEventListener('resize', this.resizeHandler);
this.handlers.clear();
this.data = [];
}
}
  1. Implementuj zarządzanie pamięcią
"Stwórz system zarządzania pamięcią:
- Object pooling dla częstych alokacji
- Słabe referencje dla cache
- Automatyczne rutyny czyszczenia
- Monitoring użycia pamięci
- Alert przy skokach pamięci"
  1. Zoptymalizuj struktury danych
"Zoptymalizuj struktury danych pod kątem efektywności pamięci:
- Używaj typed arrays gdzie stosowne
- Implementuj virtual scrolling
- Użyj IndexedDB dla dużych zbiorów danych
- Implementuj paginację danych
- Dodaj wskazówki garbage collection"
  1. Zbuduj dashboard wydajności
"Stwórz monitorowanie wydajności w czasie rzeczywistym:
- Śledź Core Web Vitals
- Monitoruj czasy odpowiedzi API
- Śledź użycie pamięci
- Alert przy regresji wydajności
- Stwórz budżety wydajności"

Przykład konfiguracji monitorowania:

monitoring/performance-monitor.ts
export class PerformanceMonitor {
private metrics: Map<string, PerformanceMetric[]> = new Map();
private observers: PerformanceObserver[] = [];
initialize() {
// Monitoruj Core Web Vitals
this.observeWebVitals();
// Monitoruj wydajność React
this.monitorReactPerformance();
// Monitoruj wywołania API
this.interceptFetch();
// Monitoruj pamięć
this.monitorMemory();
// Wysyłaj metryki co 10 sekund
setInterval(() => this.sendMetrics(), 10000);
}
private observeWebVitals() {
// Observer LCP
const lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.recordMetric('lcp', lastEntry.startTime);
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
// Observer FID
const fidObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.recordMetric('fid', entry.processingStart - entry.startTime);
}
});
fidObserver.observe({ entryTypes: ['first-input'] });
// Observer CLS
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
this.recordMetric('cls', clsValue);
}
}
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
this.observers.push(lcpObserver, fidObserver, clsObserver);
}
private monitorReactPerformance() {
if (window.React && window.React.Profiler) {
// Wrap React.Profiler aby przechwycić wszystkie rendery
const originalProfiler = window.React.Profiler;
window.React.Profiler = (props) => {
const onRender = (id, phase, actualDuration, baseDuration) => {
this.recordMetric(`react-render-${id}`, actualDuration, {
phase,
baseDuration,
timestamp: performance.now()
});
// Alert przy wolnych renderach
if (actualDuration > 16) { // próg 60fps
console.warn(`Wykryto wolny render: ${id} zajął ${actualDuration}ms`);
}
props.onRender?.(id, phase, actualDuration, baseDuration);
};
return originalProfiler({ ...props, onRender });
};
}
}
private interceptFetch() {
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const startTime = performance.now();
const url = args[0].toString();
try {
const response = await originalFetch(...args);
const duration = performance.now() - startTime;
this.recordMetric('api-call', duration, {
url,
status: response.status,
method: args[1]?.method || 'GET'
});
return response;
} catch (error) {
const duration = performance.now() - startTime;
this.recordMetric('api-error', duration, {
url,
error: error.message
});
throw error;
}
};
}
private monitorMemory() {
if (!performance.memory) return;
setInterval(() => {
const memoryInfo = performance.memory;
this.recordMetric('memory', memoryInfo.usedJSHeapSize, {
totalJSHeapSize: memoryInfo.totalJSHeapSize,
jsHeapSizeLimit: memoryInfo.jsHeapSizeLimit
});
// Alert przy skokach pamięci
const usage = memoryInfo.usedJSHeapSize / memoryInfo.jsHeapSizeLimit;
if (usage > 0.9) {
console.error('Krytyczne użycie pamięci:', `${(usage * 100).toFixed(1)}%`);
}
}, 5000);
}
private recordMetric(name: string, value: number, metadata?: any) {
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name)!.push({
value,
timestamp: Date.now(),
metadata
});
}
private async sendMetrics() {
const metricsToSend = {};
for (const [name, values] of this.metrics.entries()) {
if (values.length > 0) {
metricsToSend[name] = values;
this.metrics.set(name, []); // Wyczyść wysłane metryki
}
}
if (Object.keys(metricsToSend).length > 0) {
try {
await fetch('/api/metrics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(metricsToSend)
});
} catch (error) {
console.error('Nie udało się wysłać metryk:', error);
}
}
}
}
  1. Ustaw budżety wydajności
"Stwórz konfigurację budżetu wydajności:
- Maksymalne rozmiary bundle
- Progi czasów ładowania
- Limity użycia pamięci
- SLA czasów odpowiedzi API
- Wymagania frame rate
Zintegruj z pipeline CI/CD"
  1. Automatyzuj testowanie wydajności
"Skonfiguruj automatyczne testowanie wydajności:
- Uruchom Lighthouse przy każdym PR
- Test obciążeniowy krytycznych ścieżek użytkownika
- Testy wykrywania wycieków pamięci
- Śledzenie rozmiaru bundle
- Alerty regresji wydajności"

Profiluj przed optymalizacją:

// Profiluj komponenty React
const ProfiledComponent = () => {
return (
<Profiler id="Dashboard" onRender={onRenderCallback}>
<Dashboard />
</Profiler>
);
};
function onRenderCallback(id, phase, actualDuration) {
console.log(`${id} (${phase}) zajął ${actualDuration}ms`);
}

Optymalizuj incrementalnie:

// Krok 1: Podstawowe lazy loading
const Dashboard = lazy(() => import('./Dashboard'));
// Krok 2: Splitting na poziomie tras
const routes = [
{
path: '/dashboard',
component: lazy(() => import('./Dashboard')),
preload: true
}
];
// Krok 3: Predykcyjne prefetching
const prefetchComponent = (path) => {
const route = routes.find(r => r.path === path);
if (route?.preload) {
route.component.preload();
}
};

Typowe wzorce optymalizacji:

// Debounce kosztownych operacji
const debouncedSearch = useMemo(
() => debounce((query) => {
performSearch(query);
}, 300),
[]
);
// Użyj Web Workers dla ciężkich obliczeń
const worker = new Worker('/workers/data-processor.js');
worker.postMessage({ cmd: 'process', data: largeDataset });
// Implementuj virtual scrolling
<VirtualList
items={millionItems}
itemHeight={50}
renderItem={renderItem}
buffer={5}
/>

Problem: Przycięte scrollowanie i interakcje

Rozwiązanie:

// Użyj CSS containment
.item {
contain: layout style paint;
}
// Grupuj aktualizacje DOM
requestAnimationFrame(() => {
// Wszystkie aktualizacje DOM tutaj
});
// Użyj transform zamiast position
transform: translateX(${x}px);
// .cursorignore dla lepszego indeksowania
# Wyklucz wygenerowane pliki
dist/
build/
*.min.js
*.bundle.js
coverage/
node_modules/
.next/
out/
# Wyklucz duże pliki danych
*.csv
*.sql
data/
fixtures/
# AI może sugerować więcej wykluczeń:
"Przeanalizuj projekt i sugeruj wpisy .cursorignore
dla optymalnej wydajności indeksowania"

Korzyści:

  • 70% szybsze indeksowanie
  • Zmniejszone użycie pamięci
  • Bardziej trafne wyniki wyszukiwania
  • Lepszy kontekst AI

Analiza wydajności sterowana AI

// Regularne audyty wydajności
"Przeanalizuj tę aplikację pod kątem problemów wydajnościowych:
- Sprawdź zapytania N+1
- Znajdź niepotrzebne re-rendery
- Zidentyfikuj wycieki pamięci
- Wykryj operacje blokujące
- Przejrzyj rozmiary bundle"
// AI dostarcza:
// 1. Rankingową listę problemów
// 2. Ocenę wpływu
// 3. Sugestie napraw
// 4. Plan implementacji

Popchaj wydajność dalej:

  1. Edge computing

    • Wdróż obliczenia na lokalizacje brzegowe
    • Implementuj regionalne cache
    • Używaj service workerów efektywnie
    • Zoptymalizuj dla globalnych użytkowników
  2. Zaawansowane techniki

    • WebAssembly dla obliczeń
    • Przyspieszenie GPU z WebGL
    • Shared ArrayBuffers
    • Renderowanie OffscreenCanvas
  3. Real User Monitoring

    • Śledź wydajność według segmentu użytkowników
    • Testuj A/B optymalizacje
    • Predykcyjne alerty wydajności
    • Optymalizacja ścieżki użytkownika

Twoja optymalizacja jest udana, gdy:

  • ✅ Strona ładuje się w poniżej 2 sekund
  • ✅ Osiągnięte 60fps interakcje
  • ✅ Użycie pamięci stabilne w czasie
  • ✅ Wszystkie Core Web Vitals zielone
  • ✅ Odpowiedzi API poniżej 100ms p95
  • ✅ Rozmiar bundle zmniejszony o 60%
  • ✅ Nie wykryto wycieków pamięci
  • ✅ Budżet wydajności utrzymany

Zespoły używające tych technik osiągają:

  • 80% redukcja czasów ładowania
  • 90% mniej skarg na wydajność
  • 50% niższe koszty infrastruktury
  • 2x poprawa współczynników konwersji

Przed wdrożeniem optymalizacji:

  • Metryki baseline zarejestrowane
  • Wszystkie zmiany zmierzone
  • Nie wprowadzono regresji
  • Wycieki pamięci przetestowane
  • Rozmiar bundle sprawdzony
  • Budżet wydajności przechodzi
  • Monitorowanie skonfigurowane
  • Alerty ustawione
  1. Mierz najpierw: Nigdy nie optymalizuj bez danych
  2. User-centric: Skup się na postrzeganej wydajności
  3. Incrementalne: Małe ulepszenia się kumulują
  4. Automatyzuj: Uczyń wydajność częścią CI/CD
  5. Monitoruj zawsze: Wydajność pogarsza się z czasem
  • Początkowy audyt: 2 godziny
  • Optymalizacja frontend: 4 godziny
  • Optymalizacja backend: 3 godziny
  • Naprawy pamięci: 2 godziny
  • Konfiguracja monitorowania: 2 godziny
  • Łącznie: ~13 godzin (oszczędza setki godzin czasu użytkowników)

Opanowałeś optymalizację wydajności. Gotowy na więcej?

WebAssembly

Użyj WASM dla zadań intensywnych obliczeniowo

Edge computing

Wdróż globalnie dla minimalnego opóźnienia

Kultura wydajności

Zbuduj praktyki zespołu priorytetyzujące wydajność

Kontynuuj do Audyt i naprawy bezpieczeństwa →