React / Performance

    React Performance Optimization

    React Scan, DevTools Profiler, Million.js (block VDOM), Rollup Visualizer, useMemo/useCallback best practices i Code Splitting.

    React Scan
    Re-render viz
    Million.js
    Block VDOM
    Rollup Viz
    Bundle audit
    RC Compiler
    Auto-memo

    6 narzędzi React Performance — porównanie

    React Scan, DevTools Profiler, Why Did You Render, Million.js, Rollup Visualizer i React Compiler — typ, użycie i zastosowanie.

    Narzędzie Typ Użycie Kiedy
    React Scan Re-render visualizer Development Identyfikuj powolne komponenty wizualnie
    React DevTools Profiler Performance profiler Development Dokładna analiza render time, flame graph
    Why Did You Render Re-render debugger Development Zrozum DLACZEGO komponent re-renderuje
    Million.js Block Virtual DOM Produkcja Listy, tabele, heavy data-driven UI
    Rollup Visualizer Bundle analyzer Build Znajdź duże zależności w bundle
    React Compiler (beta) Auto-memoization Build + Runtime Automatyczne memo/useCallback, Next.js 15

    Często zadawane pytania

    Co to jest React Scan i jak diagnozować zbędne re-rendery?

    React Scan (Aiden Bai, 2024): automatyczne wykrywanie powolnych komponentów. Wizualizacja re-renderów. Koloruje komponenty które rerenderują. Instalacja: npm install react-scan. import {scan} from 'react-scan'. scan({enabled: true}). Alternatywnie: CDN script tag w HTML. React DevTools Profiler: wbudowany w React DevTools. Record -> naciśnij przycisk -> Stop. Flame graph komponentów. Render czas w ms. Sprawdź które komponenty renderują długo. Why Did You Render (@welldone-software/why-did-you-render): monkey-patches React. Wypisuje do konsoli dlaczego komponent re-renderował. Jakie props/state zmieniły się. Instalacja: npm install @welldone-software/why-did-you-render --save-dev. wdyr.js: import React from 'react'. import whyDidYouRender from '@welldone-software/why-did-you-render'. whyDidYouRender(React, {trackAllPureComponents: true}). Komponent.whyDidYouRender = true. React.memo: React.memo(Component) — memoizuje komponent. Shallow prop comparison. Nie memoizuj wszystkiego — overhead. Memoizuj: drogie obliczenia. Stabilne callbacks. Krok po kroku: 1. Zidentyfikuj problem (React Scan / Profiler). 2. Sprawdź WDYR. 3. Zdecyduj memo/callback/useMemo. 4. React Compiler — automatyczne memoizowanie. useCallback: stabilna referencja funkcji. dla event handlers przekazywanych do dzieci. Ważne: memo + useCallback razem. useMemo: dla expensive calculations. Sprawdź faktyczną korzyść. Nie optymalizuj przedwcześnie.

    Bundle Analysis — Rollup Visualizer, Bundle Phobia i webpack-bundle-analyzer?

    Rollup Visualizer (Vite plugin): npm install rollup-plugin-visualizer --save-dev. vite.config.ts: import {visualizer} from 'rollup-plugin-visualizer'. plugins: [visualizer({open: true, gzipSize: true, brotliSize: true})]. npm run build -> otwiera stats.html. Treemap bundle visualizacja. @next/bundle-analyzer (Next.js): npm install @next/bundle-analyzer. const withBundleAnalyzer = require('@next/bundle-analyzer')({enabled: process.env.ANALYZE === 'true'}). ANALYZE=true npm run build -> otwiera analyzer. webpack-bundle-analyzer: npm install webpack-bundle-analyzer. Webpack config plugin. Interaktywny treemap. Source maps required. Importmap.io: online analyzer. Wgraj bundle JS. Analiza dependencies. bundlephobia.com: sprawdź rozmiar dowolnego pakietu npm. Gzip, brotli. Peer dependencies. Alternatives (mniejsze). packagephobia.com: install size vs bundle size. Node modules size. Strategie redukcji bundle: Tree shaking: ESM modules tylko. Brak side effects. sideEffects: false w package.json. Dynamic imports: const module = await import('./heavy-module'). Lazy loading routes (React.lazy). Code splitting vendor: manual chunks w vite.config: rollupOptions: {output: {manualChunks: {vendor: ['react', 'react-dom'], ui: ['@radix-ui/react-dialog']}}}. External dependencies: externals (dla libraries). UMD build vs ESM build. Polyfill audit: core-js wymogi. @babel/preset-env browserslist. Nadmierne polyfills = bundle bloat. dead-css: PurgeCSS dla Tailwind. Automatyczne w produkcji.

    Million.js i Virtual DOM optimization — jak działa block Virtual DOM?

    Wirtualny DOM problem: React diff algorithm = O(n). Każdy render = nowe drzewo. Porównanie całego drzewa. Przy wielu komponentach: bottleneck. Million.js (Aiden Bai, 2022): block Virtual DOM. Kompilacja do wydajnych operacji. Omija React VDOM dla statycznych komponentów. Tylko dynamiczne parts są śledzone. Instalacja: npm install million. Vite plugin: import million from 'million/compiler'. plugins: [million.vite()]. Next.js: million.next(). Automatyczna optymalizacja. React API kompatybilny: nie trzeba zmieniać kodu. block() function: import {block} from 'million/react'. const MyComponent = block(function MyComponent({count}) {return div{count}/div}). Kiedy Million.js pomaga: wiele list items. Tabele. Dashboards z dużą ilością danych. Komponenty które często re-renderują. Kiedy nie pomaga: małe komponenty. Rzadkie re-rendery. Komponenty z dużo JSX branching. Million Lint: reguły ESLint dla performance. Sugeruje optymalizacje. React Forget (Compiler) vs Million: React Compiler — automatyczne memoizowanie. Million.js — inny algorytm diff. Komplementarne. Preact vs React (dla performance): Preact (~3KB) vs React (~40KB). Compat layer dla libraries. Doskonały dla performance-critical. Brak niektórych React features. Solid.js: fine-grained reactivity. Zero Virtual DOM. Najbardziej wydajny framework. Nie React — inne API. Svelte: compilation approach. Nie VDOM. Bardzo szybki. Ale nie React.

    useMemo i useCallback — kiedy naprawdę pomagają?

    Kiedy useMemo: Expensive computation: const sorted = useMemo(() => items.sort(compare), [items]). Sortowanie 10 000 elementów co render. Memoizowanie objects dla referential equality: const options = useMemo(() => ({color: 'blue', size: 'large'}), []). Bez memo: nowy obiekt = nowa referencja = child re-render. Kiedy useCallback: Callback przekazywany do memoizowanego dziecka: const handleClick = useCallback(() => setCount(c => c + 1), []). Child: React.memo(ChildComponent). Bez useCallback: nowa funkcja = nowa referencja. Kiedy NIE memo/useCallback: Prosta obliczenia (szybsze od cache overhead). Nie przekazywane do memoizowanych dzieci. Zbyt często zmieniające się dependencies. Reguła: measure first, optimize second. React Compiler eliminuje potrzebę: experimental: {reactCompiler: true} w Next.js 15. Automatyczne useMemo i useCallback. Nie musisz pisać ręcznie. Ale: React Compiler beta — sprawdź kompatybilność. Referential stability patterns: Context value memoization. const contextValue = useMemo(() => ({user, logout}), [user]). Bez memo: każdy consumer re-renderuje. State management memoization: Zustand selectors: const count = useStore(state => state.count). Stabilna referencja jeśli count nie zmienia się. Jotai: atomowe updates = minimalne re-rendery. Valtio: proxy-based = tylko subskrybenty. React Query: automatycznie memoizuje responses. Stable queryKey. Normalizacja danych: flatten nested objects. Denormalized state = więcej re-renderów. Normalizacja (jak Redux Toolkit) = precyzyjne updates.

    Code Splitting i Lazy Loading — zaawansowane strategie?

    React.lazy + Suspense: const Chart = lazy(() => import('./Chart')). Ładuj tylko gdy potrzebny. Code splitting po routach: React Router z lazy(). Next.js App Router automatyczny. Granular splitting: nie tylko routes. Komponenty heavy (Chart, Editor, Map). Lazy modals: nie renderuj Monaco Editor jeśli nie otwarty. Prefetching: link rel='prefetch' — przeglądarka ładuje gdy idle. import('/heavy').then(...) — explicit prefetch. React Router prefetch: loader w beforeLoad. Route.prefetch() — prefetch na hover. Webpack magic comments: import(/* webpackPrefetch: true */ './Chart'). import(/* webpackPreload: true */ './critical'). preload — krytyczny, ładuj równolegle. prefetch — niekoniecznie potrzebny, ładuj gdy idle. Vite rollupOptions: manualChunks. Kontrola które moduły do jakiego chunk. Vendor chunk: React, React DOM razem. UI chunk: shadcn/radix razem. Feature chunki: per feature area. Dynamic import z warunkiem: if (feature.isEnabled) {const {FeatureModule} = await import('./feature')}. Brak ładowania jeśli feature wyłączony. Streaming SSR (Next.js): Suspense boundary = streaming point. Serwer streamuje HTML gdy Suspense resolves. użytkownik widzi partial content szybciej. Partial Prerendering (PPR): statyczna powłoka + dynamic holes. Instant TTFB (statyczna część). Dynamic content ładuje się asynchronicznie. Import maps: mapuj moduły do URLi. Brak node_modules lookup. Importmap.json. useId hook: stable IDs między SSR i CSR. Brak hydration mismatch.

    Czytaj dalej

    Powiązane artykuły

    Kontakt

    Skontaktuj się z nami

    Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.

    Wyślij zapytanie

    Bezpłatna wycena w 24h
    Bez zobowiązań
    Indywidualne podejście
    Ekspresowa realizacja

    Telefon

    +48 790 814 814

    Pon-Pt: 9:00 - 18:00

    Email

    adam@fotz.pl

    Odpowiadamy w ciągu 24h

    Adres

    Plac Wolności 16

    61-739 Poznań

    Godziny pracy

    Pon - Pt9:00 - 18:00
    Sob - NdzZamknięte

    Wolisz porozmawiać?

    Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.

    Zadzwoń teraz