JavaScript pode ser um grande gargalo de performance:
<!-- ❌ Bloqueia parsing -->
<script src="script.js"></script>
<!-- ✅ Async - executa assim que baixar (não mantém ordem) -->
<script src="script.js" async></script>
<!-- ✅ Defer - executa após parsing (mantém ordem) -->
<script src="script.js" defer></script>
Async: Para scripts independentes (analytics, ads)
<script src="analytics.js" async></script>
Defer: Para scripts que dependem do DOM
<script src="app.js" defer></script>
Para JavaScript crítico necessário imediatamente:
<script>
// Código crítico inline (pequeno)
document.documentElement.classList.add('js-enabled');
</script>
Divida código em chunks menores:
// ❌ Ruim - tudo em um bundle
import { heavyFunction } from './heavy-module';
heavyFunction();
// ✅ Bom - carregamento sob demanda
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavy-module');
heavyFunction();
});
// React Router
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
// Componente pesado carregado sob demanda
const Chart = lazy(() => import('./components/Chart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<>
<button onClick={() => setShowChart(true)}>
Mostrar Gráfico
</button>
{showChart && (
<Suspense fallback={<ChartSkeleton />}>
<Chart />
</Suspense>
)}
</>
);
}
// Antes (desenvolvimento)
function calculateTotal(items) {
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
return total;
}
// Depois (produção)
function calculateTotal(a){let b=0;for(let c=0;c<a.length;c++)b+=a[c].price;return b}
Remova código não utilizado:
// ❌ Ruim - importa tudo
import * as utils from './utils';
// ✅ Bom - importa apenas o necessário
import { debounce, throttle } from './utils';
// webpack.config.js
module.exports = {
optimization: {
usedExports: true, // Tree shaking
minimize: true, // Minificação
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
<!-- Preload script crítico -->
<link rel="preload" as="script" href="critical.js">
<script src="critical.js"></script>
<!-- Prefetch script que será usado depois -->
<link rel="prefetch" as="script" href="next-page.js">
<!-- DNS prefetch -->
<link rel="dns-prefetch" href="https://api.example.com">
<!-- Preconnect (DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com">
// Debounce - executa após parar de chamar
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Uso
const handleSearch = debounce((query) => {
searchAPI(query);
}, 300);
input.addEventListener('input', (e) => {
handleSearch(e.target.value);
});
// 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);
window.addEventListener('scroll', handleScroll);
// ❌ Ruim - calcula sempre
function expensiveCalculation() {
return heavyComputation();
}
// ✅ Bom - calcula apenas quando necessário
let cachedResult = null;
function expensiveCalculation() {
if (cachedResult === null) {
cachedResult = heavyComputation();
}
return cachedResult;
}
Mova processamento pesado para worker:
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
const result = e.data;
// Usar resultado
};
// worker.js
self.onmessage = (e) => {
const { data } = e.data;
const result = heavyProcessing(data);
self.postMessage(result);
};
// ❌ 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);
}
});
Para eventos de scroll/touch que não precisam preventDefault:
// ✅ Melhor performance
window.addEventListener('scroll', handleScroll, { passive: true });
// webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
# Analisar tamanho de node_modules
npx bundle-phobia [package-name]
# Verificar duplicações
npx webpack-bundle-analyzer dist/stats.json
<!DOCTYPE html>
<html>
<head>
<!-- Preconnect para APIs -->
<link rel="preconnect" href="https://api.example.com">
<!-- Preload script crítico -->
<link rel="preload" as="script" href="/js/critical.js">
<!-- Prefetch script da próxima página -->
<link rel="prefetch" as="script" href="/js/next-page.js">
</head>
<body>
<!-- Conteúdo -->
<!-- Script crítico inline pequeno -->
<script>
// Inicialização crítica
document.documentElement.classList.add('js-enabled');
</script>
<!-- Script crítico -->
<script src="/js/critical.js"></script>
<!-- Scripts não críticos com defer -->
<script src="/js/app.js" defer></script>
<!-- Analytics com async -->
<script src="/js/analytics.js" async></script>
</body>
</html>
// app.js - com code splitting
const loadChart = async () => {
const { Chart } = await import('./components/Chart');
return Chart;
};
// Lazy load componente pesado
button.addEventListener('click', async () => {
const Chart = await loadChart();
renderChart(Chart);
});