A performance web é crucial para a experiência do usuário e SEO. Sites lentos resultam em maior taxa de abandono, menor conversão e pior posicionamento nos mecanismos de busca. Este guia apresenta técnicas avançadas para otimizar a velocidade de carregamento e responsividade.
Core Web Vitals: Métricas Essenciais
O Google definiu três métricas fundamentais que impactam diretamente o ranking:
Largest Contentful Paint (LCP)
Mede o tempo para carregar o maior elemento visível. Meta: 2.5 segundos
// Monitorando LCP
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP:', entry.startTime);
// Enviar para analytics
gtag('event', 'web_vitals', {
name: 'LCP',
value: Math.round(entry.startTime),
event_category: 'Performance'
});
}
}).observe({ entryTypes: ['largest-contentful-paint'] });
First Input Delay (FID)
Mede a responsividade à primeira interação. Meta: 100ms
// Monitorando FID
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
const FID = entry.processingStart - entry.startTime;
console.log('FID:', FID);
gtag('event', 'web_vitals', {
name: 'FID',
value: Math.round(FID),
event_category: 'Performance'
});
}
}).observe({ entryTypes: ['first-input'] });
Cumulative Layout Shift (CLS)
Mede a estabilidade visual. Meta: 0.1
// Monitorando CLS
let clsValue = 0;
let clsEntries = [];
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
clsEntries.push(entry);
}
}
console.log('CLS:', clsValue);
}).observe({ entryTypes: ['layout-shift'] });
Otimização de Imagens
Formatos Modernos
Use formatos eficientes com fallbacks:
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Descrição" loading="lazy">
</picture>
Responsive Images
<img
src="image-800w.jpg"
srcset="
image-400w.jpg 400w,
image-800w.jpg 800w,
image-1200w.jpg 1200w
"
sizes="
(max-width: 480px) 100vw,
(max-width: 768px) 50vw,
33vw
"
alt="Imagem responsiva"
loading="lazy"
>
Lazy Loading Avançado
// Intersection Observer para lazy loading customizado
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
}, {
rootMargin: '50px 0px' // Carrega 50px antes de entrar na viewport
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
Otimização de CSS
Critical CSS
Extraia e inline o CSS crítico:
<!-- CSS crítico inline -->
<style>
/* Estilos para above-the-fold */
.header { background: #fff; height: 60px; }
.hero { min-height: 400px; background: #f5f5f5; }
</style>
<!-- CSS não-crítico carregado assincronamente -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
CSS Containment
/* Otimiza recálculos de layout */
.card {
contain: layout style paint;
}
.sidebar {
contain: layout;
}
/* Para componentes independentes */
.widget {
contain: strict;
}
Otimização de JavaScript
Code Splitting
// Webpack - divisão por rota
import('./pages/HomePage').then(module => {
const HomePage = module.default;
// Renderizar componente
});
// React - lazy loading
const HomePage = React.lazy(() => import('./pages/HomePage'));
function App() {
return (
<Suspense fallback={<div>Carregando...</div>}>
<HomePage />
</Suspense>
);
}
Tree Shaking
// ❌ Importa toda a biblioteca
import _ from 'lodash';
// ✅ Importa apenas o necessário
import debounce from 'lodash/debounce';
// ✅ Ainda melhor - use alternativas nativas
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
Web Workers para Tarefas Pesadas
// worker.js
self.onmessage = function(e) {
const { data, operation } = e.data;
let result;
switch (operation) {
case 'sort':
result = data.sort((a, b) => a - b);
break;
case 'filter':
result = data.filter(item => item > 100);
break;
}
self.postMessage(result);
};
// main.js
const worker = new Worker('worker.js');
function processLargeDataset(data) {
return new Promise((resolve) => {
worker.postMessage({ data, operation: 'sort' });
worker.onmessage = (e) => resolve(e.data);
});
}
// Uso
processLargeDataset(largeArray).then(sortedData => {
console.log('Dados processados:', sortedData);
});
Otimização de Rede
Resource Hints
<!-- Preconnect para domínios externos -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://api.example.com">
<!-- DNS prefetch para recursos futuros -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- Preload para recursos críticos -->
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="main.css" as="style">
<!-- Prefetch para recursos da próxima página -->
<link rel="prefetch" href="next-page.html">
Service Workers para Cache
// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/styles.css',
'/script.js',
'/offline.html'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit - retorna resposta
if (response) {
return response;
}
// Clona a requisição
const fetchRequest = event.request.clone();
return fetch(fetchRequest).then(response => {
// Verifica se é uma resposta válida
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clona a resposta
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
Otimização de Fontes
Font Loading Strategy
/* CSS */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-display: swap; /* Mostra fallback até carregar */
}
/* Preload de fontes críticas */
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
/* Font loading API */
if ('fonts' in document) {
const font = new FontFace('CustomFont', 'url(font.woff2)');
font.load().then(() => {
document.fonts.add(font);
document.body.classList.add('font-loaded');
});
}
Monitoramento de Performance
Performance API
// Medindo performance customizada
performance.mark('task-start');
// Executar tarefa
await heavyTask();
performance.mark('task-end');
performance.measure('task-duration', 'task-start', 'task-end');
const measures = performance.getEntriesByType('measure');
console.log('Duração da tarefa:', measures[0].duration);
// Limpeza
performance.clearMarks();
performance.clearMeasures();
Real User Monitoring (RUM)
// Coletando métricas reais
function sendMetrics() {
const navigation = performance.getEntriesByType('navigation')[0];
const metrics = {
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
ttfb: navigation.responseStart - navigation.requestStart,
download: navigation.responseEnd - navigation.responseStart,
domReady: navigation.domContentLoadedEventEnd - navigation.navigationStart,
loadComplete: navigation.loadEventEnd - navigation.navigationStart
};
// Enviar para analytics
fetch('/analytics', {
method: 'POST',
body: JSON.stringify(metrics),
headers: { 'Content-Type': 'application/json' }
});
}
// Executar após carregamento completo
window.addEventListener('load', () => {
setTimeout(sendMetrics, 0);
});
Otimização de Banco de Dados
Query Optimization
-- ❌ Query lenta
SELECT * FROM posts
WHERE LOWER(title) LIKE '%javascript%'
ORDER BY created_at DESC;
-- ✅ Query otimizada
SELECT id, title, excerpt, created_at
FROM posts
WHERE title_search @@ to_tsquery('javascript')
ORDER BY created_at DESC
LIMIT 20;
-- Índice para busca full-text
CREATE INDEX idx_posts_search ON posts USING gin(title_search);
Database Connection Pooling
// Node.js com pool de conexões
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
database: 'mydb',
user: 'user',
password: 'password',
max: 20, // máximo de conexões
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Uso eficiente
async function getUser(id) {
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0];
} finally {
client.release(); // Importante: liberar conexão
}
}
Ferramentas de Análise
Lighthouse CI
// lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000'],
numberOfRuns: 3
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'categories:best-practices': ['error', { minScore: 0.9 }],
'categories:seo': ['error', { minScore: 0.9 }]
}
},
upload: {
target: 'temporary-public-storage'
}
}
};
Bundle Analyzer
// webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
})
]
};
Checklist de Performance
Imagens
- ✅ Formatos modernos (WebP, AVIF)
- ✅ Lazy loading implementado
- ✅ Responsive images configuradas
- ✅ Compressão otimizada
CSS
- ✅ Critical CSS inline
- ✅ CSS não-crítico carregado assincronamente
- ✅ Minificação habilitada
- ✅ Unused CSS removido
JavaScript
- ✅ Code splitting implementado
- ✅ Tree shaking configurado
- ✅ Minificação e compressão
- ✅ Scripts não-críticos com defer/async
Rede
- ✅ HTTP/2 habilitado
- ✅ Compressão gzip/brotli
- ✅ CDN configurado
- ✅ Cache headers otimizados
Conclusão
A otimização de performance web é um processo contínuo que requer monitoramento constante e ajustes baseados em dados reais. As técnicas apresentadas, quando aplicadas sistematicamente, podem resultar em melhorias significativas na velocidade de carregamento e experiência do usuário.
Lembre-se: performance não é apenas sobre velocidade, mas sobre criar uma experiência fluida e responsiva que mantenha os usuários engajados. Meça, otimize, monitore e repita - este é o ciclo para manter sua aplicação sempre performática.