NextJs: export statique avec routes dynamiques

Aug 31 2020

Je suis un peu confus par la documentation et je ne sais pas si c'est possible ce que j'essaie de faire.

But:

  • Exportez l'application NextJS de manière statique et hébergez-la sur netlify
  • Autoriser les utilisateurs à créer des publications et à avoir des liens vers ces publications qui fonctionnent

Par example:

  • L'utilisateur crée un nouveau message avec l'ID: 2
  • Ce message doit être accessible au public sous mysite.com/posts/2

J'imagine que je peux créer un fichier html squelette appelé posts.htmlet netlify redirige toutes les posts/<id>demandes vers ce posts.htmlfichier qui affichera ensuite le squelette et chargera les données nécessaires via une API à la volée.

Je pense que sans ce hack netlify, mon objectif d'exportation statique + liens fonctionnels vers des routes dynamiques n'est pas possible avec next.js selon leur documentation car ce fallback: truen'est possible qu'avec SSR.

Question: Comment puis-je réaliser la configuration de mes rêves d'exportation statique nextjs + liens de travail vers des routes dynamiques?

EDIT: Je viens de découvrir les redirections . Ils pourraient être la solution à mon problème.

Réponses

4 narmageddon Sep 03 2020 at 11:14

getStaticProps et getStaticPaths ()

Il semble que l'utilisation de getStaticProps et getStaticPaths () soit la voie à suivre.

J'ai quelque chose comme ça dans mon [post].jsfichier:

const Post = ({ pageContent }) => {
  // ...
}

export default Post;

export async function getStaticProps({ params: { post } }) {
  const [pageContent] = await Promise.all([getBlogPostContent(post)]);
  return { props: { pageContent } };
}

export async function getStaticPaths() {
  const [posts] = await Promise.all([getAllBlogPostEntries()]);

  const paths = posts.entries.map((c) => {
    return { params: { post: c.route } }; // Route is something like "this-is-my-post"
  });

  return {
    paths,
    fallback: false,
  };
}

Dans mon cas, j'interroge Contentful en utilisant mon getAllBlogPostEntriespour les entrées de blog. Cela crée les fichiers, quelque chose comme this-is-my-post.html. getBlogPostContent(post)récupérera le contenu du fichier spécifique.

export async function getAllBlogPostEntries() {
  const posts = await client.getEntries({
    content_type: 'blogPost',
    order: 'fields.date',
  });
  return posts;
}

export async function getBlogPostContent(route) {
  const post = await client.getEntries({
    content_type: 'blogPost',
    'fields.route': route,
  });
  return post;
}

Lorsque je fais un, npm run exportil crée un fichier pour chaque article de blog ...

info  - Collecting page data ...[
  {
    params: { post: 'my-first-post' }
  },
  {
    params: { post: 'another-post' }
  },

Dans votre cas, ce routeserait juste 1, 2, 3, etc.


Méthode obsolète - Exécutez une requête dans next.config.js

Si vous cherchez à créer un site statique, vous devrez interroger les publications à l'avance, avant le next export.

Voici un exemple d'utilisation de Contentful que vous avez peut-être configuré avec des articles de blog:

Créez d'abord une page sous pages/blog/[post].js.

Ensuite, vous pouvez utiliser un exportMapintérieur next.config.js.

// next.config.js
const contentful = require('contentful');

// Connects to Contentful
const contentfulClient = async () => {
  const client = await contentful.createClient({
    space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID,
    accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN,
  });
  return client;
};

// Gets all of the blog posts
const getBlogPostEntries = async (client) => {
  const entries = await client.getEntries({
    content_type: 'blogPost',
    order: 'fields.date',
  });
  return entries;
};

module.exports = {
  async exportPathMap() {
    const routes = {
      '/': { page: '/' }, // Index page
      '/blog/index': { page: '/blog' }, // Blog page
    };

    const client = await contentfulClient();
    const posts = await getBlogPostEntries(client);

    // See explanation below
    posts.items.forEach((item) => {
      routes[`/blog/${item.fields.route}`] = { page: '/blog/[post]' };
    });

    return routes;
  },
};

Juste au-dessus, return routes;je me connecte à Contentful et je récupère tous les articles du blog. Dans ce cas, chaque message a une valeur que j'ai définie appelée route. J'ai donné à chaque élément de contenu une valeur d'itinéraire, quelque chose comme this-is-my-first-postet just-started-blogging. En fin de compte, l'objet route ressemble à quelque chose comme ceci:

routes = {
  '/': { page: '/' }, // Index page
  '/blog/index': { page: '/blog' }, // Blog page
  '/blog/this-is-my-first-post': { page: '/blog/[post]' },
  '/blog/just-started-blogging': { page: '/blog/[post]' },
};

Votre exportation dans le out/répertoire sera:

out/
   /index.html
   /blog/index.html
   /blog/this-is-my-first-post.html
   /blog/just-started-blogging.html

Dans votre cas, si vous utilisez des numéros d'identification de publication, vous devrez récupérer les articles du blog et faire quelque chose comme:

const posts = await getAllPosts();

posts.forEach((post) => {
  routes[`/blog/${post.id}`] = { page: '/blog/[post]' };
});

// Routes end up like
// routes = {
//   '/': { page: '/' }, // Index page
//   '/blog/index': { page: '/blog' }, // Blog page
//   '/blog/1': { page: '/blog/[post]' },
//   '/blog/2': { page: '/blog/[post]' },
// };

L'étape suivante consisterait à créer une sorte de hook sur Netlify pour déclencher une construction de site statique lorsque l'utilisateur crée du contenu.

Voici également une idée de ce à quoi vous pages/blog/[post].jsressembleriez.

import Head from 'next/head';

export async function getBlogPostContent(route) {
  const post = await client.getEntries({
    content_type: 'blogPost',
    'fields.route': route,
  });
  return post;
}

const Post = (props) => {
  const { title, content } = props;
  return (
    <>
      <Head>
        <title>{title}</title>
      </Head>
      {content}
    </>
  );
};

Post.getInitialProps = async ({ asPath }) => {
  // asPath is something like `/blog/this-is-my-first-post`
  const pageContent = await getBlogPostContent(asPath.replace('/blog/', ''));
  const { items } = pageContent;
  const { title, content } = items[0].fields;
  return { title, content };
};

export default Post;