feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# Next.js — Best Practices
|
||||
|
||||
> Extraído de skills: `next-best-practices` v0.1.0 + `nextjs-patterns` v1.0.0
|
||||
|
||||
## 🏗️ Estrutura de Projeto (App Router)
|
||||
```
|
||||
app/
|
||||
├── (marketing)/ # Route group — não aparece na URL
|
||||
│ └── page.tsx
|
||||
├── (dashboard)/
|
||||
│ ├── layout.tsx # Layout compartilhado do grupo
|
||||
│ ├── [id]/
|
||||
│ │ └── page.tsx # Página dinâmica
|
||||
│ └── loading.tsx # Suspense boundary automático
|
||||
├── api/
|
||||
│ └── route.ts # Route Handler (REST/GraphQL)
|
||||
├── error.tsx # Error boundary por rota
|
||||
├── not-found.tsx # Página 404
|
||||
└── layout.tsx # Root layout (obrigatório)
|
||||
```
|
||||
|
||||
## 🖥️ Server vs Client Components
|
||||
```tsx
|
||||
// ✅ Server Component (padrão) — roda no servidor, nenhum JS no cliente
|
||||
async function ProductList() {
|
||||
// DB query acontece no servidor!
|
||||
const products = await db.product.findMany();
|
||||
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
||||
}
|
||||
|
||||
// ✅ Client Component — só quando precisar de interatividade
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
return <button onClick={() => setCount(n => n + 1)}>{count}</button>;
|
||||
}
|
||||
|
||||
// ✅ Server Actions — mutations sem criar API routes
|
||||
async function createProduct(formData: FormData) {
|
||||
"use server";
|
||||
await db.product.create({ data: { ... } });
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 Padrões de Data Fetching
|
||||
|
||||
| Padrão | Quando usar | Exemplo |
|
||||
|--------|-------------|---------|
|
||||
| **Server Component `await`** | Read, página ou componente | `const posts = await db.post.findMany()` |
|
||||
| **Server Actions** | Write/mutations, formulários | `"use server"` + `revalidatePath` |
|
||||
| **Route Handlers** | REST/GraphQL API, webhooks, integrações | `app/api/users/route.ts` |
|
||||
| **`use()` hook** | Ler promise em Client Components | `const data = use(fetchData())` |
|
||||
|
||||
### Evitando Data Waterfalls
|
||||
```tsx
|
||||
// ❌ Ruim — sequencial, cada await aguarda o anterior
|
||||
async function Page() {
|
||||
const user = await getUser(); // 200ms
|
||||
const posts = await getPosts(); // 300ms (só começa após user)
|
||||
return <Profile user={user} posts={posts} />;
|
||||
}
|
||||
|
||||
// ✅ Bom — paralelo
|
||||
async function Page() {
|
||||
const [user, posts] = await Promise.all([
|
||||
getUser(), // começa imediatamente
|
||||
getPosts(), // começa imediatamente
|
||||
]);
|
||||
return <Profile user={user} posts={posts} />;
|
||||
}
|
||||
```
|
||||
|
||||
## 🗄️ Database Client Singleton
|
||||
```ts
|
||||
// lib/db.ts — criar uma vez por request (Server Component boundary)
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const globalForDb = globalThis as unknown as { prisma: PrismaClient };
|
||||
|
||||
export const db = globalForDb.prisma ?? new PrismaClient();
|
||||
if (process.env.NODE_ENV !== 'production') globalForDb.prisma = db;
|
||||
```
|
||||
|
||||
## 🔄 Caching — 4 Camadas Next.js
|
||||
|
||||
| Camada | O que cacheia | Controlado por |
|
||||
|--------|---------------|----------------|
|
||||
| **Request Memoization** | `fetch`, `React.cache()` | automático por request |
|
||||
| **Data Cache** | `fetch` responses | `next: { revalidate: 60 }` |
|
||||
| **Full Route Cache** | Render completo | `export const revalidate` |
|
||||
| **Router Cache** | Estado do cliente após navegação | `next/router` |
|
||||
|
||||
```ts
|
||||
// Revalidar após 60s
|
||||
fetch(url, { next: { revalidate: 60 } });
|
||||
|
||||
// Não cachear (sempre fresh)
|
||||
fetch(url, { next: { revalidate: 0 } });
|
||||
```
|
||||
|
||||
## ⚠️ Regra Fundamental
|
||||
> **`"use client"` é uma barreira de escape.** Tudo dentro de um arquivo com essa diretiva
|
||||
> roda no navegador. Tudo SEM ela roda no servidor. Quando possível, fique no servidor.
|
||||
Reference in New Issue
Block a user