14 points par xguru 2025-02-24 | 3 commentaires | Partager sur WhatsApp
  • L’orientation future de Next.js est passionnante
  • Il y a eu des problèmes autour des Server Actions, mais React 19 avec useOptimistic et useFormStatus laisse entrevoir des améliorations possibles
    • L’approche useFetcher de Remix offre aussi une bonne DX
  • Le PPR (Partial Pre-rendering) de Next.js et le nouveau système de cache granulaire se démarquent particulièrement
  • Dans l’ensemble, l’impression est très positive

The Big Picture

  • Le nouveau système de cache peut être activé de façon expérimentale dans next.config.js
  • Il est possible de définir des profils de cache pour configurer différentes durées d’expiration et fréquences de revalidation
// next.config.js  
const config = {  
  experimental: {  
    // Active le nouveau système de cache. On peut désormais utiliser `use cache` dans le code  
    dynamicIO: true,   
    // Optionnel : configuration des profils de cache  
    cacheLife: {  
      blog: {  
        stale: 3600, // conservation du cache côté client : 1 heure  
        revalidate: 900, // rafraîchissement côté serveur : 15 minutes  
        expire: 86400, // durée de vie maximale : 1 jour  
      },  
    },  
  },  
};  

Utilisation de base de use cache

  • La mise en cache est possible au niveau du fichier, du composant ou de la fonction via la directive "use cache"
  • L’exemple montre qu’il suffit d’ajouter use cache pour appliquer facilement le cache
  • cacheTag, revalidateTag et autres permettent d’invalider le cache au moment souhaité
// 1. Mise en cache au niveau du fichier  
"use cache";  
export default function Page() {  
  return <div>Cached Page</div>;  
}  
  
// 2. Mise en cache au niveau du composant  
export async function PriceDisplay() {  
  "use cache";  
  const price = await fetchPrice();  
  return <div>${price}</div>;  
}  
  
// 3. Mise en cache au niveau de la fonction  
export async function getData() {  
  "use cache";  
  return await db.query();  
}  

Cache basé sur des tags

import { unstable_cacheTag as cacheTag, revalidateTag } from 'next/cache';  
  
// Mettre en cache un groupe de données spécifique  
export async function ProductList() {  
  'use cache';  
  cacheTag('products');  
  const products = await fetchProducts();  
  return <div>{products}</div>;  
}  
  
// Invalider le cache quand les données changent  
export async function addProduct() {  
  'use server';  
  await db.products.add(...);  
  revalidateTag('products');  
}  

Profils de cache personnalisés

  • unstable_cacheLife permet de récupérer les profils de cache définis dans next.config.js
  • Le profil déclaré dans le code (par exemple "blog") est utilisé pour appliquer la politique de cache
import { unstable_cacheLife as cacheLife } from "next/cache";  
  
export async function BlogPosts() {  
  "use cache";  
  cacheLife("blog"); // utilisation du profil de cache prédéfini pour le blog  
  return await fetchPosts();  
}  

Points importants mais faciles à manquer

Génération automatique des clés de cache

  • Les props et arguments du composant sont automatiquement inclus dans la clé de cache
  • Les valeurs non sérialisables (comme les fonctions) sont traitées comme des « références non modifiables »
export async function UserCard({ id, onDelete }) {  
  "use cache";  
  // id est inclus dans la clé de cache  
  // onDelete est transmis mais n’a pas d’effet sur la mise en cache  
  const user = await fetchUser(id);  
  return <div onClick={onDelete}>{user.name}</div>;  
}  

Mélange de contenu dynamique et de contenu mis en cache

  • On peut mixer les deux en passant du contenu dynamique comme enfant à l’intérieur d’un contenu mis en cache
  • Un tableau de cacheTag permet d’appliquer et d’invalider plusieurs tags à la fois
export async function CachedWrapper({ children }) {  
  "use cache";  
  const header = await fetchHeader();  
  return (  
    <div>  
      <h1>{header}</h1>  
      {children} {/* le contenu dynamique reste tel quel */}  
    </div>  
  );  
}  
export async function ProductPage({ id }) {  
  "use cache";  
  cacheTag(["products", `product-${id}`, "featured"]);  
  // l’invalidation est possible via n’importe lequel de ces tags  
}  

Hiérarchie de cache

  • Si "use cache" est déclaré au niveau le plus haut, toute la zone correspondante est mise en cache
  • Certaines parties spécifiques (par exemple des sections dynamiques avec Suspense) peuvent être exclues de cette zone de cache
"use cache";  
export default async function Page() {  
  return (  
    <div>  
      <CachedHeader />  
      <div>  
        <Suspense fallback={<Loading />}>  
          <DynamicFeed /> {/* contenu dynamique */}  
        </Suspense>  
      </div>  
    </div>  
  );  
}  

Sûreté de type

  • Les chaînes comme les clés de cache et les profils de cache peuvent être gérées comme des constantes pour réduire l’usage de magic strings
  • À l’image des patterns de React Query, utiliser une approche qui génère les tags est pratique
// Gérer les clés de profil de cache comme des constantes  
export const CACHE_LIFE_KEYS = {  
  blog: "blog",  
} as const;  
  
const config = {  
  experimental: {  
    cacheLife: {  
      [CACHE_LIFE_KEYS.blog]: {  
        stale: 3600,  
        revalidate: 900,  
        expire: 86400,  
      },  
    },  
  },  
};  

Comment gérer efficacement les tags de cache

  • Application d’un tag factory pattern à la manière de 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;  
  
// Définir les tags de cache  
function tagCache(tags: string[]) {  
  cacheTag(...tags);  
}  
  
// Exemple d’utilisation  
export async function BlogList() {  
  "use cache";  
  tagCache(CACHE_TAGS.blog.list());  
}  

3 commentaires

 
schang124 2025-03-03

Je pense qu’il vaut mieux utiliser un framework comme Next.js ou Remix uniquement dans les cas où le SSR est nécessaire parce que le SEO est important.

En particulier, je pense qu’il faut être prudent avant d’adopter Next.js pour des services où le SEO n’est pas important, comme les produits B2B ou les back offices. En effet, les interfaces imposées par Next.js et sa complexité peuvent réduire la productivité du développement.

Personnellement, je trouve que lorsque le SEO n’est pas nécessaire, Vite + React est bien meilleur en termes de productivité de développement et de flexibilité.

 
[Ce commentaire a été masqué.]
 
9vvin 2025-02-25

Depuis la version 13, next.js est devenu vraiment utilisable, mais récemment je l’apprécie vraiment, vraiment beaucoup. J’ai l’impression qu’il va devenir le standard de fait de la stack technologique du développement web full-stack.