NextJs:动态路由静态导出

NextJs: Static export with dynamic routes

我对文档有点困惑,不确定我正在尝试做的事情是否可行。

目标:

例如:

我想象我可以创建一个名为 posts.html 的框架 html 文件,netlify 将所有 posts/<id> 请求重定向到那个 posts.html 文件,然后显示框架并通过 API 动态加载必要的数据。

我认为如果没有这个 netlify hack,根据他们的 documentation,我的静态导出 + 到动态路由的工作链接的目标是不可能的,因为 fallback: true 只有在使用 SSR。

问题:如何实现静态 nextjs 导出 + 动态路由的工作链接的梦想设置?

编辑: 我刚知道 Redirects。它们可能是我问题的解决方案。

getStaticPropsgetStaticPaths()

看起来使用 getStaticPropsgetStaticPaths() 是可行的方法。

我的 [post].js 文件中有这样的内容:

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,
  };
}

就我而言,我使用 getAllBlogPostEntries 查询 Contentful 以获取博客条目。这会创建文件,类似于 this-is-my-post.htmlgetBlogPostContent(post) 将抓取特定文件的内容。

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;
}

当我执行 npm run export 时,它会为每个博客创建一个文件 post...

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

在您的情况下,route 将只是 1、2、3 等


过时的方法 - 运行 next.config.js

中的查询

如果您要创建静态站点,则需要在 next export.

之前提前查询 post

这是一个使用 Contentful 的示例,您可能已经在博客 posts 中设置了它:

首先在pages/blog/[post].js下创建一个页面。

接下来可以在next.config.js里面用一个exportMap

// 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;
  },
};

就在 return routes; 上面,我正在连接到 Contentful,并获取所有博客 post。在这种情况下,每个 post 都有一个我定义的名为 route 的值。我为每条内容都指定了一个路由值,例如 this-is-my-first-postjust-started-blogging。最后,路由对象看起来像这样:

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]' },
};

您在 out/ 目录中的导出将是:

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

在您的情况下,如果您使用 post id 号码,则必须获取博客 posts 并执行以下操作:

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]' },
// };

下一步是在 Netlify 上创建某种挂钩,以在用户创建内容时触发静态站点构建。

这里还有关于您的 pages/blog/[post].js 的想法。

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;