Aperçu rapide de la fonctionnalité bêta de Next.js 13
Next.js 13 a atterri de manière quelque peu confuse. Beaucoup de choses remarquables ont été ajoutées ; cependant, une bonne partie est encore bêta. Néanmoins, les fonctionnalités bêta nous donnent des signaux importants sur la façon dont l’avenir de Next.js sera façonné, il y a donc de bonnes raisons de les surveiller de près, même si vous allez attendre pour les adopter.
Cet article fait partie d’une série d’expériences sur les fonctionnalités bêta. Aujourd’hui, examinons la partie la moins mature – la nouvelle modèles de récupération de données.
Avant Next.js 13, les modèles de récupération de données au niveau de la page sont assez simples :
- Si votre page est (principalement) statique, implémentez un
getStaticProps
pour récupérer les données afin que la récupération se produise au moment de la construction (et à ISR temps). - Si votre page est dynamique, implémentez un
getServerSideProps
pour récupérer les données par requête côté serveur. - Pour les données qui dépendent de l’interaction de l’utilisateur, effectuez une récupération côté client dans le
useEffect
hook après le rendu de la page.
Celui-ci a été entièrement rénové dans Next.js 13 (si vous optez pour les fonctionnalités expérimentales). Mais avant de voir les nouveautés, réfléchissons aux problèmes de l’ancien monde.
1. Cela ne semble pas naturel
export default function Page({ data }) {
// Render data...
}export async function getServerSideProps() {
const res = await fetch(`https://.../data`)
const data = await res.json()
return { props: { data } }
}
Le seul but de getServerSideProps
est de calculer les accessoires pour Page
, mais vous devez les mettre côte à côte en tant que deux fonctions distinctes. Je ne suis généralement pas contre les API basées sur les conventions, mais ce modèle semble inutilement verbeux.
2. Cela implique assez souvent un « perçage d’hélice »
Le «perçage d’accessoires» se produit lorsque vous avez un objet d’état complexe quelque part très haut dans l’arbre de rendu et que vous devez en transmettre des morceaux (ou le tout) en profondeur. C’est à la fois lourd à écrire et difficile à lire.
Bien que le forage d’hélice ne soit pas spécifique à SSR ou SSG, les fonctions de récupération de données – getStaticProps
et getServerSideProps
– en sont des sources naturelles. Parce que lors de l’adoption de SSR/SSG, vous souhaitez récupérer tout ce qui est possible côté serveur pour une page, et le modèle de récupération des données nécessite que cela se produise dans un endroit centralisé ; vous devrez propager un gros morceau d’accessoires dans l’arbre.
Il existe des solutions pour le forage d’accessoires, comme l’utilisation de l’API contextuelle ou modèle de composition de composantmais cela entrave la réutilisation de vos composants ou vous oblige à les concevoir avec soin.
Pour le dire en d’autres termes, la cause première du problème est que la récupération de données ne peut pas colocaliser avec les composants qui utilisent les données.
3. C’est tout ou rien
Lors de l’utilisation getServerSideProps
que le chargement d’une page soit déclenché par un rechargement de navigateur ou un routage côté client, le nouveau contenu de la page n’est pas affiché tant que la récupération des données n’est pas complètement terminée (l’async getServerSideProps
la fonction résout). Cela peut être problématique si votre page contient à la fois des « données rapides » et des « données lentes ». Par exemple, le tableau de bord des données est un scénario typique : certaines cartes peuvent se charger instantanément, tandis que d’autres peuvent prendre plusieurs secondes.
Les nouveaux modèles de récupération de données introduits dans Next.js 13 ont supprimé tout ce que vous connaissez : getStaticProps
, getServerSideProps
et même pour la récupération côté client, il existe une nouvelle use
crochet qui remplacera potentiellement l’ancienne méthode de récupération useEffect
.
Les nouveaux modèles sont profondément couplés aux composants serveur React. Si vous ne le connaissez pas, il est bon de consulter l’article précédent de la série :
Attention, les nouvelles fonctionnalités sont encore assez expérimentales et je ne recommanderais pas de les utiliser en production.
Composants de serveur asynchrone
Dans l’ancien monde, les composants étaient synchrones et vous ne pouviez pas await
au niveau supérieur d’un composant. Dans Next.js 13, tous les composants sont par défaut « serveur » et peuvent être asynchrones. Enfin, nous pouvons utiliser notre syntaxe async/wait familière dans les composants React.
Cela rend la récupération de données beaucoup plus facile et plus flexible. La meilleure chose est que vous pouvez distribuer votre logique de récupération côté serveur à plusieurs endroits et les colocaliser avec des composants qui utilisent les données.
Regardons un exemple avec deux composants récupérant tous deux des citations aléatoires à partir de l’API :
// app/server-async-fetching/page.js// a page containing a fast-loading and a slow-loading components,
// both are server components
import Quote from '../../components/server/Quote';
export default function AsyncLoading() {
return (
<div>
<Quote />
<Quote slow={true} />
</div>
);
}
// lib/quote.js// utility for fetching random famous quotes from API, allowing simulation of a
// slow request
import sleep from 'sleep-promise';
export async function getQuote(delay = 0) {
if (delay) {
await sleep(delay);
}
return (
await fetch('https://api.quotable.io/random?tags=technology')
).json();
}
// components/server/Quote.jsimport { getQuote } from '../../lib/quote';
import os from 'os';
export default async function Quote() {
const quote = await getQuote(slow ? 2000 : 0);
return (
<div>
<p>
{slow ? 'Slow' : 'Fast'} component rendered on
<span>${os.hostname()}</span>
</p>
<blockquote>
{quote.content}
</blockquote>
</div>
);
}
Cela a fonctionné, avec un code async/wait plus propre; cependant, il souffre toujours du problème du « tout ou rien ». La page n’est rendue que lorsque les composants Fast et Slow ont tous deux terminé la récupération.
Nous pouvons l’améliorer avec une petite correction en ajoutant <Suspense />
autour des composants. Suspense a été initialement ajouté par React pour soutenir fractionnement de code; maintenant, il peut être utilisé pour fournir une interface utilisateur de secours pour les composants asynchrones qui ne sont pas encore résolus, afin qu’ils puissent s’afficher sans bloquer :
export default function AsyncLoading() {
return (
<>
<div>
<Suspense fallback={<p>Fast component loading...</p>}>
<Quote />
</Suspense><Suspense fallback={<p>Slow component loading...</p>}>
<Quote slow={true} />
</Suspense>
</div>
</>
);
}
Bien mieux maintenant. Vous pouvez voir que le rendu de la page et de ses deux composants enfants est entièrement asynchrone. React a étendu la puissance de Suspense pour prendre en charge les opérations asynchrones arbitraires. Il fonctionne désormais parfaitement avec les composants serveur asynchrones.
Ce qui est cool avec Suspense, c’est que « rétablir la suspension » d’un composant ne nécessite pas de requêtes API supplémentaires ni de connexion WebSocket. Au lieu de cela, le nouveau contenu de la page est transmis au navigateur en ajoutant un DOM virtuel (encapsulé dans <script/>
) au document HTML. Cela peut être confirmé en regardant le moment de la demande de document :
Déduplication automatique fetch
Une autre chose intéressante que vous avez peut-être déjà remarquée est que bien que les composants Fast et Slow aient fait des demandes d’API (avec fetch) séparément, ils ont obtenu le même contenu de devis. Cela est dû à une autre mise à jour importante de React — l’appel de récupération (côté serveur) est automatiquement dédupliqué :
Si vous devez récupérer les mêmes données dans plusieurs composants d’un arbre (par exemple, l’utilisateur actuel), Next.js mettra automatiquement en cache
fetch
requêtes qui ont la même entrée dans un cache temporaire. — https://beta.nextjs.org/docs/data-fetching/fundamentals
Cela nous aide à résoudre l’autre problème avec les anciens modèles de récupération de données – le forage d’accessoires. Avec la déduplication de récupération automatique, la récupération de la même ressource dans une passe de rendu ne génère qu’une seule requête HTTP, vous avez donc la liberté de récupérer les données là où vous en avez besoin sans vous soucier du coût supplémentaire. Plutôt cool, n’est-ce pas ?
Récupération de données côté client
Dans les versions précédentes de Next.js (et React), la récupération de données côté client n’est pas concernée par le framework. Vous pouvez utiliser la bibliothèque de votre choix et des outils tiers tels que TOS et requête de réaction fait un travail fantastique pour résoudre ce problème.
Next.js 13 (devrait plus honnêtement dire le dernier React) a fait un pas en avant en offrant un use
hook en tant qu’API générale pour déballer les données des promesses. Ce n’est pas aussi idéal que d’utiliser directement async
/ await
(comme expliqué par React), mais cela rend la récupération côté client assez proche du côté serveur.
Voyons à nouveau comment cela fonctionne avec un exemple (tous les composants composants clients tels qu’ils sont marqués par ‘use client’):
// app/client-fetching/page.js'use client';
import { useState, Suspense } from 'react';
import Quote from '../../components/client/Quote';
export default function ClientFetching() {
// use a button to toggle the loading of components to make sure
// they're rendered on the client-side
const [show, setShow] = useState(false);
return (
<>
<h1>Client Fetching</h1>
<button onClick={() => setShow(true)}>
Show Components
</button>
{show && (
<>
<div>
<Suspense fallback={<p>Fast component loading...</p>}>
<Quote />
</Suspense>
<Suspense fallback={<p>Slow component loading...</p>}>
<Quote slow={true} />
</Suspense>
</div>
</>
)}
</>
);
}
// components/client/Quote.js'use client';
import { getQuote } from '../../lib/quote';
import { use } from 'react';
const quoteFetch = getQuote();
const quoteFetchSlow = getQuote(2000);
export default function Quote({ slow }) {
const quote = use(slow ? quoteFetchSlow : quoteFetch);
return (
<div>
<p>{slow ? 'Slow' : 'Fast'} component rendered</p>
<blockquote>{quote.content}</blockquote>
</div>
);
}
C’est plus propre que ce à quoi nous étions habitués : aller chercher useEffect
et stocker le résultat dans une variable d’état. Il est également assez proche de son apparence dans les composants du serveur.
Un lecteur prudent aurait peut-être remarqué que le résultat était différent de ce que nous avons vu avec les composants serveur. Vous avez raison; les deux composants ont rendu deux citations différentes. Le fetch
l’appel n’est pas dédupliqué côté client. Je ne sais pas si c’est par conception ou quelque chose à corriger (je l’espère). Cependant, depuis le use
hook est générique et fonctionne avec n’importe quelle promesse, je suppose que vous pouvez implémenter une couche de cache autour fetch
sans problème.
Le nouveau modèle de récupération de données est un énorme changement et semble excitant mais, en même temps, un peu déroutant. La documentation contient de vagues descriptions de ce qui peut ou ne peut pas être fait à plusieurs endroits. La prise en charge de la dactylographie est également incomplète. Je pense que c’est principalement parce que sa base – le composant serveur asynchrone de React et use
crochet – sont si neufs et non polis. Il reste encore un long chemin à parcourir avant que cette pièce ne soit prête à être utilisée en production.
J’aime l’idée des composants serveur asynchrones. C’est un excellent moyen de déplacer davantage de calculs vers le côté serveur, de réduire la taille du bundle client et de préserver un modèle de programmation relativement cohérent à travers la frontière du réseau. Cependant, en même temps, je prévois également de rencontrer beaucoup de confusion, d’anti-modèle et de bogues mystérieux lorsque je commencerai à l’adopter sérieusement.
Vous pouvez trouver le code du projet de démonstration iciet les autres histoires de la série ici :
Merci d’avoir lu!
Want to Connect?I'm the creator of ZenStack, a toolkit for building
secure CRUD services with Next.js + TypeScript.
Our goal is to let you save time writing boilerplate code
and focus on building what matters - the user experience.