14 pontos por xguru 2025-02-24 | 3 comentários | Compartilhar no WhatsApp
  • A direção futura do Next.js é empolgante
  • Houve questões em torno de Server Actions, mas parece haver potencial de melhora com useOptimistic e useFormStatus do React 19
    • A abordagem useFetcher do Remix também oferece uma boa DX
  • O PPR (Partial Pre-rendering) do Next.js e o novo sistema de cache granular se destacam especialmente
  • No geral, a impressão é muito positiva

The Big Picture

  • É possível ativar experimentalmente o novo sistema de cache em next.config.js
  • É possível definir perfis de cache para configurar diferentes tempos de expiração e ciclos de revalidação
// next.config.js
const config = {
  experimental: {
    // Ativa o novo sistema de cache. Agora é possível usar `use cache` no código
    dynamicIO: true,
    // Opcional: configuração de perfis de cache
    cacheLife: {
      blog: {
        stale: 3600, // manter cache no cliente: 1 hora
        revalidate: 900, // atualizar no servidor: 15 minutos
        expire: 86400, // vida útil máxima: 1 dia
      },
    },
  },
};

Uso básico de use cache

  • É possível fazer cache em nível de arquivo, componente e função com a declaração "use cache"
  • Nos exemplos de código, basta adicionar use cache para aplicar cache com facilidade
  • Também é possível invalidar o cache no momento desejado usando cacheTag, revalidateTag etc.
// 1. Cache no nível de arquivo
"use cache";
export default function Page() {
  return Cached Page

;
}

// 2. Cache no nível de componente
export async function PriceDisplay() {
  "use cache";
  const price = await fetchPrice();
  return ${price}

;
}

// 3. Cache no nível de função
export async function getData() {
  "use cache";
  return await db.query();
}

Cache baseado em tags

import { unstable_cacheTag as cacheTag, revalidateTag } from 'next/cache';

// Faz cache de um grupo específico de dados
export async function ProductList() {
  'use cache';
  cacheTag('products');
  const products = await fetchProducts();
  return {products}

;
}

// Invalida o cache quando os dados mudam
export async function addProduct() {
  'use server';
  await db.products.add(...);
  revalidateTag('products');
}

Perfil de cache personalizado

  • É possível usar unstable_cacheLife para carregar os perfis de cache definidos em next.config.js
  • A política de cache é aplicada usando o nome do perfil declarado no código (ex.: "blog")
import { unstable_cacheLife as cacheLife } from "next/cache";

export async function BlogPosts() {
  "use cache";
  cacheLife("blog"); // usa o perfil de cache de blog pré-definido
  return await fetchPosts();
}

Pontos importantes que podem passar despercebidos

Geração automática de chave de cache

  • As props e os arguments do componente são incluídos automaticamente na chave de cache
  • Valores não serializáveis (como funções) são tratados na forma de “referência imutável”
export async function UserCard({ id, onDelete }) {
  "use cache";
  // id é incluído na chave de cache
  // onDelete é passado, mas não afeta o cache
  const user = await fetchUser(id);
  return {user.name}

;
}

Mistura de conteúdo dinâmico e conteúdo em cache

  • É possível combinar ambos passando conteúdo dinâmico como filho dentro de conteúdo em cache
  • Também é possível definir um array em cacheTag para aplicar e invalidar várias tags ao mesmo tempo
export async function CachedWrapper({ children }) {
  "use cache";
  const header = await fetchHeader();
  return (

      {header}

      {children} {/* conteúdo dinâmico é mantido como está */}

  );
}
export async function ProductPage({ id }) {
  "use cache";
  cacheTag(["products", `product-${id}`, "featured"]);
  // é possível invalidar usando qualquer uma dessas tags
}

Hierarquia de cache

  • Ao declarar "use cache" no nível mais alto, toda aquela área passa a ser armazenada em cache
  • Partes específicas (por exemplo, seções dinâmicas com Suspense) podem ficar fora da área de cache
"use cache";
export default async function Page() {
  return (

           {/* conteúdo dinâmico */}

  );
}

Segurança de tipos

  • Strings como chaves de cache e perfis de cache podem ser gerenciadas como constantes para reduzir o uso de magic strings
  • É prático usar uma abordagem que gere tags, no estilo dos padrões do React Query
// gerenciar chaves de perfil de cache como constantes
export const CACHE_LIFE_KEYS = {
  blog: "blog",
} as const;

const config = {
  experimental: {
    cacheLife: {
      [CACHE_LIFE_KEYS.blog]: {
        stale: 3600,
        revalidate: 900,
        expire: 86400,
      },
    },
  },
};

Como gerenciar tags de cache com eficiência

  • Aplicar o padrão de factory de tags no estilo React Query
export const CACHE_TAGS = {
  blog: {
    all: ["blog"] as const,
    list: () => [...CACHE_TAGS.blog.all, "list"] as const,
    post: (id: string) => [...CACHE_TAGS.blog.all, "post", id] as const,
    comments: (postId: string) =>
      [...CACHE_TAGS.blog.all, "post", postId, "comments"] as const,
  },
} as const;

// Definição de tags de cache
function tagCache(tags: string[]) {
  cacheTag(...tags);
}

// Exemplo de uso
export async function BlogList() {
  "use cache";
  tagCache(CACHE_TAGS.blog.list());
}

3 comentários

 
schang124 2025-03-03

Parece melhor usar frameworks como Next.js ou Remix apenas quando o SEO é importante e há necessidade de SSR.

Acho que é preciso ter cautela ao adotar Next.js em serviços nos quais SEO não é importante, como produtos B2B ou back office. Isso porque a interface e a complexidade impostas pelo Next.js podem reduzir a produtividade no desenvolvimento.

Pessoalmente, acho que, quando SEO não é necessário, Vite + React é muito melhor em termos de produtividade de desenvolvimento e flexibilidade.

 
[Este comentário foi ocultado.]
 
9vvin 2025-02-25

O Next.js ficou bem utilizável desde a versão 13, mas ultimamente tenho gostado dele de verdade. Acho que vai se tornar o padrão de fato da stack tecnológica de desenvolvimento web full stack.