Cette série est mon CV d’étude Next.js, et malgré son désir d’un Next.js vanille, toutes les fonctionnalités sont applicables avec le SDK Sitecore. C’est semblable au guide J’ai récemment écrit sur GraphQL et vise à réduire la courbe d’apprentissage pour ceux qui y passent à partir d’autres piles technologiques.
- Dans partie 1 nous avons abordé certains principes fondamentaux de Next.js : les stratégies de rendu ainsi que les nuances de getStaticProps, getStaticPaths, getServerSideProps ainsi que la récupération de données.
- Dans partie 2 nous avons parlé des éléments liés à l’interface utilisateur à venir OOB avec Next.js – mises en page, styles et polices, fonctionnalités puissantes, composants d’image et de script, et bien sûr – TypeScript.
Dans cet article, nous allons parler du routage avec Next.js – pages, routes API, mises en page et middleware.
Routage
Le routage Next.js est basé sur la notion de pages. Un fichier situé dans le pages
le répertoire devient automatiquement une route. Index.js
les fichiers font référence au répertoire racine :
pages/index.js
->/
pages/blog/index.js
->/blog
Le routeur prend en charge les fichiers imbriqués :
pages/blog/first-post.js
->/blog/first-post
pages/dashboard/settings/username.js
->/dashboard/settings/username
Vous pouvez également définir des segments d’itinéraire dynamiques à l’aide de crochets :
pages/blog/[slug].js
->/blog/:slug
(Par exemple:blog/first-post
)pages/[username]/settings.js
->/:username/settings
(Par exemple:/johnsmith/settings
)pages/post/[...all].js
->/post/*
(Par exemple:/post/2021/id/title
)
Navigation entre les pages
Vous devriez utiliser le Link
composant pour le routage côté client :
import Link from 'next/link' export default function Home() { return ( <ul> <li> <Link href="https://blogs.perficient.com/"> Home </Link> </li> <li> <Link href="http://blogs.perficient.com/about"> About </Link> </li> <li> <Link href="http://blogs.perficient.com/blog/first-post"> First post </Link> </li> </ul> ) }
Donc nous avons:
/
→pages/index.js
/about
→pages/about.js
/blog/first-post
→pages/blog/[slug].js
Pour les segments dynamiques, n’hésitez pas à utiliser l’interpolation :
import Link from 'next/link' export default function Post({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}> <Link href={`/blog/${encodeURIComponent(post.slug)}`}> {post.title} </Link> </li> ))} </ul> ) }
Ou un effet de levier URL
objet:
import Link from 'next/link' export default function Post({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}> <Link href={{ pathname: '/blog/[slug]', query: { slug: post.slug }, }} > <a>{post.title}</a> </Link> </li> ))} </ul> ) }
Ici, nous passons :
pathname
est le nom de la page sous lepages
répertoire (/blog/[slug]
dans ce cas)query
est un objet ayant un segment dynamique (slug
dans ce cas)
Pour accéder au router
objet dans un composant, vous pouvez utiliser le useRouter
crochet ou le withRouter
utilitaire, et il est recommandé d’utiliser useRouter
.
Itinéraires dynamiques
Si vous souhaitez créer un itinéraire dynamique, vous devez ajouter [param]
au chemin de la page.
Considérons une page pages/post/[pid].js
ayant le code suivant :
import { useRouter } from 'next/router' export default function Post() { const router = useRouter() const { id } = router.query return <p>Post: {id}</p> }
Dans ce scénario, les itinéraires /post/1
, /post/abc
etc. correspondront pages/post/[id].js
. Le paramètre correspondant est transmis à une page en tant que paramètre de chaîne de requête, avec d’autres paramètres.
Par exemple, pour l’itinéraire /post/abc
le query
l’objet ressemblera à : { "id": "abc" }
Et pour le parcours /post/abc?foo=bar
comme ça: { "id": "abc", "foo": "bar" }
Les paramètres de route écrasent les paramètres de chaîne de requête, de sorte que le query
objet pour le /post/abc?id=123
l’itinéraire ressemblera à ceci : { "id": "abc" }
Pour les itinéraires comportant plusieurs segments dynamiques, la requête est formée exactement de la même manière. Par exemple, la page pages/post/[id]/[cid].js
correspondra à l’itinéraire /post/123/456
et la requête ressemblera à ceci : { "id": "123", "cid": "456" }
La navigation entre les itinéraires dynamiques côté client est gérée à l’aide de next/link
:
import Link from 'next/link' export default function Home() { return ( <ul> <li> <Link href="https://blogs.perficient.com/post/abc"> Leads to `pages/post/[id].js` </Link> </li> <li> <Link href="http://blogs.perficient.com/post/abc?foo=bar"> Also leads to `pages/post/[id].js` </Link> </li> <li> <Link href="http://blogs.perficient.com/post/123/456"> <a>Leads to `pages/post/[id]/[cid].js`</a> </Link> </li> </ul> ) }
Attraper tous les itinéraires
Les itinéraires dynamiques peuvent être étendus pour capturer tous les chemins en ajoutant des points de suspension (...
) entre crochets. Par exemple, pages/post/[...slug].js
correspondra /post/a
, /post/a/b
, /post/a/b/c
etc.
Veuillez noter: slug
n’est pas défini de manière stricte, vous pouvez donc utiliser le nom de votre choix, par exemple : [...param]
.
Les paramètres correspondants sont transmis à la page en tant que paramètres de chaîne de requête (slug
dans ce cas) avec une valeur de tableau. Par exemple, une requête pour /post/a
aura la forme suivante : {"slug": ["a"]}
et pour /post/a/b
celui-ci: {"slug": ["a", "b"]}
Routes d’interception tous les chemins peuvent être facultatifs – pour cela, le paramètre doit être entouré d’un crochet supplémentaire ([[...slug]]
). Par exemple, pages/post/[[...slug]].js
correspondra /post
, /post/a
, /post/a/b
etc.
Les itinéraires fourre-tout sont ceux que Sitecore utilise par défaut et peuvent être trouvés sur src\[your_nextjs_all_name]\src\pages\[[...path]].tsx
.
La principale différence entre les « catchers » réguliers et facultatifs est que les facultatifs correspondent à un itinéraire sans paramètres (/post
dans notre cas).
Exemples de query
objet:
{ } // GET `/post` (empty object) { "slug": ["a"] } // `GET /post/a` (single element array) { "slug": ["a", "b"] } // `GET /post/a/b` (array with multiple elements)
Veuillez noter les caractéristiques suivantes :
- Les routes statiques ont priorité sur les routes dynamiques, et les routes dynamiques ont priorité sur les routes fourre-tout, par exemple :
pages/post/create.js
– correspondra/post/create
pages/post/[id].js
– correspondra/post/1
,/post/abc
etc., mais pas/post/create
pages/post/[...slug].js
– correspondra/post/1/2
,/post/a/b/c
etc., mais pas/post/create
et/post/abc
- les pages traitées à l’aide de l’optimisation statique automatique seront hydratées sans paramètres de routage, c’est-à-dire
query
sera un objet vide ({}
). Après hydratation, la mise à jour de l’application remplira lequery
.
Approche impérative de la navigation côté client
Comme je l’ai mentionné ci-dessus, dans la plupart des cas, Link
composant de next/link
serait suffisant pour implémenter la navigation côté client. Cependant, vous pouvez également exploiter le routeur depuis next/router
pour ça:
import { useRouter } from 'next/router' export default function ReadMore() { const router = useRouter() return ( <button onClick={() => router.push('/about')}> Read about </button> ) }
Routage peu profond
Le routage superficiel vous permet de modifier les URL sans redémarrer les méthodes pour obtenir des données, y compris le getServerSideProps
et getStaticProps
les fonctions. Nous recevons la mise à jour pathname
et query
à travers le router
objet (obtenu en utilisant useRouter()
ou withRouter()
) sans perdre l’état du composant.
Pour activer le routage superficiel, définissez { shallow: true }
:
import { useEffect } from 'react' import { useRouter } from 'next/router' // current `URL` is `/` export default function Page() { const router = useRouter() useEffect(() => { // perform navigation after first rendering router.push('?counter=1', undefined, { shallow: true }) }, []) useEffect(() => { // value of `counter` has changed! }, [router.query.counter]) }
Lors de la mise à jour de l’URL, seulement l’État de l’itinéraire va changer.
Veuillez noter: le routage superficiel ne fonctionne que sur une seule page. Disons que nous avons un pages/about.js
page et nous procédons comme suit :
router.push('?counter=1', '/about?counter=1', { shallow: true })
Dans ce cas, la page actuelle est déchargée, une nouvelle est chargée et les méthodes de récupération de données sont réexécutées (indépendamment de la présence de { shallow: true }
).
Itinéraires API
Tout fichier situé sous le pages/api
le dossier est mappé vers /api/*
et est considéré comme un point de terminaison d’API, pas une page. En raison de sa nature non-interface utilisateur, le code de routage reste côté serveur et n’augmente pas la taille du bundle client. L’exemple ci-dessous pages/api/user.js
renvoie un code d’état de 200
et données au format JSON :
export default function handler(req, res) { res.status(200).json({ name: 'Martin Miles' }) }
Le handler
la fonction reçoit deux paramètres :
req
– un exemple dehttp.IncomingMessage
+ plusieurs middlewares intégrés (expliqué ci-dessous)res
– un exemple dehttp.ServerResponse
+ quelques fonctions d’assistance (expliqué ci-dessous)
Vous pouvez utiliser req.method
pour gérer diverses méthodes:
export default function handler(req, res) { if (req.method === 'POST') { // handle POST request } else { // handle other request types } }
Cas d’utilisation
L’intégralité de l’API peut être construite à l’aide d’une interface de routage afin que l’API existante reste intacte. D’autres cas pourraient être :
- masquer l’URL d’un service externe
- en utilisant des variables d’environnement (stocké sur le serveur) pour accéder aux services externes en toute sécurité
Nuances
- L’interface de routage ne traite pas les en-têtes CORS par défaut. Cela se fait à l’aide d’un middleware (voir ci-dessous)
- l’interface de routage ne peut pas être utilisée avec
next export
Quant aux segments de routage dynamique, ils sont soumis aux mêmes règles que les parties dynamiques des routages de pages que j’ai expliquées plus haut.
Intergiciels
L’interface de routage comprend les middlewares suivants qui transforment la requête entrante (req
) :
req.cookies
– un objet contenant les cookies inclus dans la requête (la valeur par défaut est{}
)req.query
– un objet contenant la chaîne de requête (la valeur par défaut est{}
)req.body
– un objet contenant le corps de la requête commeContent-Type
en-tête, ounull
Personnalisations du middleware
Chaque itinéraire peut exporter un config
objet avec les paramètres Middleware :
export const config = { api: { bodyParser: { sizeLimit: '2mb' } } }
bodyParser: false
– désactive l’analyse des réponses (renvoie le flux de données brutes sous la formeStream
)bodyParser.sizeLimit
– la taille maximale du corps de la requête dans tout format pris en charge par octetsexternalResolver: true
– indique au serveur que cette route est en cours de traitement par un résolveur externe, tel qu’express ou connect
Ajout de middlewares
Envisageons d’ajouter cors middleware. Installez le module en utilisant npm install cors
et ajouter cors
à l’itinéraire :
import Cors from 'cors' // initialize middleware const cors = Cors({ methods: ['GET', 'HEAD'] }) // helper function waiting for a successful middleware resolve // before executing some other code // or to throw an exception if middleware fails const runMiddleware = (req, res, next) => new Promise((resolve, reject) => { fn(req, res, (result) => result instanceof Error ? reject(result) : resolve(result) ) }) export default async function handler(req, res) { // this actually runs middleware await runMiddleware(req, res, cors) // the rest `API` logic res.json({ message: 'Hello world!' }) }
Fonctions d’assistance
L’objet de réponse (res
) comprend un ensemble de méthodes pour améliorer l’expérience de développement et accélérer la création de nouveaux points de terminaison.
Cela comprend les éléments suivants :
res.status(code)
– fonction de définition du code d’état de la réponseres.json(body)
– d’envoyer une réponse au format JSON,body
devrait être n’importe quel objet sérialisableres.send(body)
– pour envoyer une réponse,body
pourrait être unstring
,object
ouBuffer
res.redirect([status,] path)
– pour rediriger vers la page spécifiée,status
par défaut307
(redirection temporaire)
Ceci conclut la partie 3. Dans la partie 4, nous parlerons de la mise en cache, de l’authentification et des considérations liées à la mise en ligne.
Source link