Performance em Aplicações Next.js: Guia Completo
Quando você trabalha em aplicações que recebem milhões de acessos, como no QuintoAndar e Netshoes, performance não é luxo - é necessidade. Aqui estão as técnicas que realmente funcionam em produção.
1. Image Optimization - O Game Changer
O componente Image
do Next.js é revolucionário, mas precisa ser usado corretamente:
import Image from 'next/image';
// ❌ Evite
<img src="/hero.jpg" alt="Hero" />
// ✅ Faça assim
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Para imagens above the fold
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
Resultados Reais:
- -70% no tamanho das imagens
- +40% no Largest Contentful Paint (LCP)
- Lazy loading automático
2. Code Splitting Inteligente
Dynamic Imports para Componentes Pesados
import dynamic from "next/dynamic";
// ❌ Carrega tudo de uma vez
import HeavyChart from "../components/HeavyChart";
// ✅ Carrega apenas quando necessário
const HeavyChart = dynamic(() => import("../components/HeavyChart"), {
loading: () => <div>Carregando gráfico...</div>,
ssr: false, // Se não precisar de SSR
});
Bundle Analysis
# Instale o analisador
npm install @next/bundle-analyzer
# Analise seus bundles
ANALYZE=true npm run build
3. API Routes Otimizadas
Cache Strategies
// api/posts/[id].js
export default async function handler(req, res) {
// Cache por 1 hora, stale-while-revalidate por 1 dia
res.setHeader(
"Cache-Control",
"public, s-maxage=3600, stale-while-revalidate=86400"
);
const post = await getPost(req.query.id);
res.json(post);
}
Database Query Optimization
// ❌ N+1 Query Problem
const posts = await Post.findAll();
for (const post of posts) {
post.author = await User.findById(post.authorId);
}
// ✅ Single Query com Join
const posts = await Post.findAll({
include: [{ model: User, as: "author" }],
});
4. Static Generation vs SSR
Quando usar cada um:
// ✅ Static Generation - Páginas que mudam pouco
export async function getStaticProps() {
const posts = await getPosts();
return {
props: { posts },
revalidate: 3600, // ISR - revalida a cada hora
};
}
// ✅ SSR - Páginas com dados dinâmicos/personalizados
export async function getServerSideProps({ req }) {
const session = await getSession(req);
const userData = await getUserData(session.userId);
return {
props: { userData },
};
}
5. Otimizações de Runtime
React.memo e useMemo Estratégicos
// ❌ Re-renderiza sempre
const ExpensiveComponent = ({ data, filters }) => {
const filteredData = data.filter((item) => filters.includes(item.category));
return <List items={filteredData} />;
};
// ✅ Otimizado
const ExpensiveComponent = React.memo(({ data, filters }) => {
const filteredData = useMemo(
() => data.filter((item) => filters.includes(item.category)),
[data, filters]
);
return <List items={filteredData} />;
});
6. Web Vitals em Produção
Monitoring Real User Metrics
// _app.js
export function reportWebVitals(metric) {
// Envie para seu analytics
analytics.track("Web Vital", {
name: metric.name,
value: metric.value,
id: metric.id,
});
}
Métricas que Realmente Importam:
- LCP < 2.5s (Largest Contentful Paint)
- FID < 100ms (First Input Delay)
- CLS < 0.1 (Cumulative Layout Shift)
7. Deployment e CDN
Vercel Configuration
// next.config.js
module.exports = {
experimental: {
optimizeCss: true,
},
images: {
domains: ["example.com"],
formats: ["image/webp", "image/avif"],
},
compress: true,
swcMinify: true,
};
Resultados Medidos
Implementando essas técnicas no QuintoAndar:
- Performance Score: 65 → 95
- LCP: 4.2s → 1.8s
- Bundle Size: -45%
- Time to Interactive: -60%
Ferramentas Essenciais
- Lighthouse - Auditoria básica
- WebPageTest - Análise detalhada
- Bundle Analyzer - Análise de bundles
- React DevTools Profiler - Profile de componentes
Conclusão
Performance é um processo contínuo. Estabeleça métricas, monitore constantemente e otimize baseado em dados reais de usuários.
Próximo passo: Implementar uma estratégia de monitoring para acompanhar essas métricas em produção.
Qual técnica você achou mais útil? Tem alguma dúvida sobre implementação? Compartilhe nos comentários!