NextJs: exportación estática con rutas dinámicas
Estoy un poco confundido por la documentación y no estoy seguro de si es posible lo que estoy tratando de hacer.
Objetivo:
- Exportar la aplicación NextJS de forma estática y alojarla en netlify
- Permitir a los usuarios crear publicaciones y tener enlaces a estas publicaciones que funcionan
Por ejemplo:
- El usuario crea una nueva publicación con la identificación: 2
- Esta publicación debe ser de acceso público en
mysite.com/posts/2
Me imagino que puedo crear un archivo html esqueleto llamado posts.htmly netlify redirige todas las posts/<id>solicitudes a ese posts.htmlarchivo que luego mostrará el esqueleto y cargará los datos necesarios a través de una API sobre la marcha.
Creo que sin este truco de netlify, mi objetivo de exportación estática + enlaces de trabajo a rutas dinámicas no es posible con next.js de acuerdo con su documentación, ya fallback: trueque solo es posible cuando se usa SSR.
Pregunta: ¿Cómo puedo lograr la configuración de mis sueños de exportación de nextjs estáticos + enlaces de trabajo a rutas dinámicas?
EDITAR: Me acabo de enterar de los redireccionamientos . Podrían ser la solución a mi problema.
Respuestas
getStaticProps y getStaticPaths ()
Parece que usar getStaticProps y getStaticPaths () es el camino a seguir.
Tengo algo como esto en mi [post].jsarchivo:
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,
};
}
En mi caso, consulto Contentful usando my getAllBlogPostEntriespara las entradas del blog. Eso crea los archivos, algo así como this-is-my-post.html. getBlogPostContent(post)tomará el contenido del archivo específico.
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;
}
Cuando lo hago npm run export, crea un archivo para cada publicación de blog ...
info - Collecting page data ...[
{
params: { post: 'my-first-post' }
},
{
params: { post: 'another-post' }
},
En su caso route, solo sería 1, 2, 3, etc.
Método desactualizado: ejecute una consulta en next.config.js
Si está buscando crear un sitio estático, deberá consultar las publicaciones con anticipación, antes del next export.
Aquí hay un ejemplo usando Contentful que podría haber configurado con publicaciones de blog:
Primero cree una página debajo pages/blog/[post].js.
Siguiente puede usar un exportMapinterior 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;
},
};
Justo arriba, return routes;me estoy conectando a Contentful y obteniendo todas las publicaciones del blog. En este caso, cada publicación tiene un valor que he definido llamado ruta. Le he dado a cada contenido un valor de ruta, algo como this-is-my-first-posty just-started-blogging. Al final, el objeto de ruta se parece a esto:
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]' },
};
Su exportación en el out/directorio será:
out/
/index.html
/blog/index.html
/blog/this-is-my-first-post.html
/blog/just-started-blogging.html
En su caso, si está utilizando números de identificación de publicación, tendría que buscar las publicaciones del blog y hacer algo como:
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]' },
// };
El siguiente paso sería crear algún tipo de gancho en Netlify para activar la construcción de un sitio estático cuando el usuario crea contenido.
También aquí tienes una idea de cómo te pages/blog/[post].jsverías.
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;