【问题标题】:NextJs: Static export with dynamic routesNextJs:动态路由的静态导出
【发布时间】:2020-12-19 17:11:46
【问题描述】:

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

目标:

  • 静态导出 NextJS 应用并将其托管在 netlify 上
  • 允许用户创建帖子并拥有指向这些有效帖子的链接

例如:

  • 用户创建一个 ID 为 2 的新帖子
  • 此帖子应在mysite.com/posts/2 下公开访问

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

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

问题:我怎样才能实现我梦想的静态 nextjs 导出 + 工作链接到动态路由的设置?

编辑: 我刚刚发现了Redirects。它们可能是我的问题的解决方案。

【问题讨论】:

    标签: next.js netlify


    【解决方案1】:

    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 时,它会为每篇博文创建一个文件...

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

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


    过时方法 - 在 next.config.js 中运行查询

    如果您要创建静态网站,则需要提前查询帖子,在 next export 之前。

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

    首先在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,并抓取所有博客文章。在这种情况下,每个帖子都有一个我定义的值,称为路由。我已经给每一个内容一个路由值,比如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
    

    在您的情况下,如果您使用的是帖子 ID 号,则必须获取博客帖子并执行以下操作:

    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;
    
    

    【讨论】:

    • 这是否适用于在运行 npm run export 之后创建的博客文章?我正在尝试让用户创建与我导出和部署分开的预订的工作。
    猜你喜欢
    • 2020-10-09
    • 1970-01-01
    • 2022-06-12
    • 2020-09-08
    • 2021-08-12
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    相关资源
    最近更新 更多