Autosustentabilidade

Código Eficiente

Técnicas para escrever código que consome menos recursos.

Por que Código Eficiente?

Código eficiente reduz:

  • Consumo de energia: Menos processamento = menos energia
  • Uso de CPU: Processamento otimizado
  • Uso de memória: Menos alocação de memória
  • Tempo de execução: Código mais rápido

Princípios de Código Eficiente

1. Fazer Menos

A melhor otimização é não fazer nada:

// ❌ Ruim - processa mesmo quando não precisa
function processData(data) {
  const result = expensiveOperation(data);
  if (condition) {
    return result;
  }
  return null;
}

// ✅ Bom - só processa se necessário
function processData(data) {
  if (!condition) {
    return null;
  }
  return expensiveOperation(data);
}

2. Cache Inteligente

Evite recálculos desnecessários:

// ❌ Ruim - recalcula sempre
function getExpensiveValue(input) {
  return heavyComputation(input);
}

// ✅ Bom - cache resultado
const cache = new Map();
function getExpensiveValue(input) {
  if (cache.has(input)) {
    return cache.get(input);
  }
  const result = heavyComputation(input);
  cache.set(input, result);
  return result;
}

3. Lazy Evaluation

Calcule apenas quando necessário:

// ❌ Ruim - calcula tudo
const results = data.map(expensiveFunction).filter(condition);

// ✅ Bom - lazy evaluation
const results = data
  .filter(condition)
  .map(expensiveFunction);

Otimização de Loops

Evitar Loops Desnecessários

// ❌ Ruim - múltiplos loops
const doubled = numbers.map(n => n * 2);
const filtered = doubled.filter(n => n > 10);
const sum = filtered.reduce((a, b) => a + b, 0);

// ✅ Bom - um loop
const sum = numbers
  .map(n => n * 2)
  .filter(n => n > 10)
  .reduce((a, b) => a + b, 0);

Early Exit

// ❌ Ruim - processa tudo
function findItem(items, id) {
  return items.filter(item => item.id === id)[0];
}

// ✅ Bom - para quando encontra
function findItem(items, id) {
  for (const item of items) {
    if (item.id === id) {
      return item;
    }
  }
  return null;
}

Otimização de DOM

Batch DOM Updates

// ❌ Ruim - múltiplas atualizações
items.forEach(item => {
  const element = document.createElement('div');
  element.textContent = item.name;
  document.body.appendChild(element);
});

// ✅ Bom - atualização em batch
const fragment = document.createDocumentFragment();
items.forEach(item => {
  const element = document.createElement('div');
  element.textContent = item.name;
  fragment.appendChild(element);
});
document.body.appendChild(fragment);

Virtual Scrolling

// Renderizar apenas itens visíveis
function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    startIndex + Math.ceil(containerHeight / itemHeight),
    items.length
  );
  
  const visibleItems = items.slice(startIndex, endIndex);
  
  return (
    <div onScroll={(e) => setScrollTop(e.target.scrollTop)}>
      {visibleItems.map((item, index) => (
        <div key={startIndex + index} style={{ height: itemHeight }}>
          {item.content}
        </div>
      ))}
    </div>
  );
}

Otimização de Event Handlers

Debounce e Throttle

// Debounce - executa após parar
function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  };
}

// Throttle - executa no máximo uma vez por período
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// Uso
const handleScroll = throttle(() => {
  updatePosition();
}, 100);

Event Delegation

// ❌ Ruim - muitos listeners
document.querySelectorAll('.button').forEach(button => {
  button.addEventListener('click', handleClick);
});

// ✅ Bom - um listener
document.addEventListener('click', (e) => {
  if (e.target.matches('.button')) {
    handleClick(e);
  }
});

Otimização de Memória

Evitar Memory Leaks

// Limpar event listeners
useEffect(() => {
  const handleResize = () => {
    // ...
  };
  window.addEventListener('resize', handleResize);
  
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

Weak References

// Usar WeakMap para cache que pode ser coletado
const cache = new WeakMap();

function getCachedValue(obj) {
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  const value = computeValue(obj);
  cache.set(obj, value);
  return value;
}

Code Splitting

Lazy Loading de Módulos

// Carregar apenas quando necessário
const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  const [show, setShow] = useState(false);
  
  return (
    <>
      <button onClick={() => setShow(true)}>Carregar</button>
      {show && (
        <Suspense fallback={<Loading />}>
          <HeavyComponent />
        </Suspense>
      )}
    </>
  );
}

Route-based Splitting

// Carregar rotas sob demanda
const routes = [
  {
    path: '/',
    component: lazy(() => import('./Home'))
  },
  {
    path: '/about',
    component: lazy(() => import('./About'))
  }
];

Otimização de Algoritmos

Escolher Algoritmo Apropriado

// ❌ Ruim - O(n²)
function findDuplicates(arr) {
  const duplicates = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        duplicates.push(arr[i]);
      }
    }
  }
  return duplicates;
}

// ✅ Bom - O(n)
function findDuplicates(arr) {
  const seen = new Set();
  const duplicates = new Set();
  for (const item of arr) {
    if (seen.has(item)) {
      duplicates.add(item);
    } else {
      seen.add(item);
    }
  }
  return Array.from(duplicates);
}

Web Workers

Mover Processamento Pesado

// main.js
const worker = new Worker('worker.js');

worker.postMessage({ data: largeArray });

worker.onmessage = (e) => {
  const result = e.data;
  updateUI(result);
};

// worker.js
self.onmessage = (e) => {
  const { data } = e.data;
  const result = heavyProcessing(data);
  self.postMessage(result);
};

Checklist de Otimização

  • Early exit em loops e condições
  • Cache de resultados caros
  • Lazy evaluation quando possível
  • Batch DOM updates
  • Debounce/throttle em eventos frequentes
  • Event delegation
  • Code splitting implementado
  • Memory leaks evitados
  • Algoritmos eficientes
  • Web Workers para processamento pesado

Exemplo Completo

// Componente otimizado
function OptimizedList({ items }) {
  const [filter, setFilter] = useState('');
  const [visibleCount, setVisibleCount] = useState(50);
  
  // Memoizar filtro
  const filteredItems = useMemo(() => {
    if (!filter) return items;
    return items.filter(item => 
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [items, filter]);
  
  // Virtual scrolling
  const visibleItems = useMemo(() => {
    return filteredItems.slice(0, visibleCount);
  }, [filteredItems, visibleCount]);
  
  // Debounce do filtro
  const debouncedSetFilter = useMemo(
    () => debounce(setFilter, 300),
    []
  );
  
  // Lazy load mais itens
  const loadMore = useCallback(() => {
    setVisibleCount(prev => prev + 50);
  }, []);
  
  return (
    <div>
      <input 
        onChange={(e) => debouncedSetFilter(e.target.value)}
        placeholder="Filtrar..."
      />
      <VirtualList 
        items={visibleItems}
        onLoadMore={loadMore}
      />
    </div>
  );
}
Medir antes de otimizar. Use DevTools Performance para identificar gargalos reais antes de otimizar código que pode não ser problema.