【问题标题】:Does Nextjs generate duplicate pages at build time?Nextjs 在构建时会生成重复页面吗?
【发布时间】:2021-11-21 17:01:34
【问题描述】:

所以我在任何地方都没有找到这个答案,我想知道这一点,所以这就是为什么在这里发帖。假设我有一个要使用 SSG 和 ISR 的博客。 SSG 用于主页,ISR 用于各个帖子。

因此,如果我在构建时使用 SSG 生成主页以显示 10 个帖子,代码如下:

index.js 文件:

export const getStaticProps = async () => {

    const posts = await get10PostsFromDB();

    return {
        props: {
            posts
        }
    }

}

然后,如果我对每个帖子都使用此代码并使用 getStaticPaths 来生成这 10 个帖子:

[帖子].js

export const getStaticPaths = async () => {

    const posts = await get10PostsFromDB();

    const paths = posts.map( ( item ) => ( {
        params: {
            slug: item.slug
        }
    } ) );

    return {
        paths,
        fallback: 'blocking'
    };

}


export const getStaticProps = async ({ params }) => {

    const post = await getASinglePostFromDB( params.slug );

    return {
        props: { 
            post 
        },
        revalidate: 1,
        notFound: true
    }

}

是否会有重复的 SSG 生成页面,因为在构建主页时生成了 10 个页面,然后又为 getStaticPaths 生成了这 10 个页面?还是我的想法完全错误?

【问题讨论】:

    标签: javascript reactjs next.js


    【解决方案1】:

    我认为您对 getStaticPathsgetStaticProps 的作用有点误解。

    简短回答:不,它不会复制页面。

    让我解释一下原因:

    getStaticPropsgetStaticPaths

    • getStaticProps(静态生成):在构建时获取数据。
    • getStaticPaths(静态生成):指定动态路由以根据数据预渲染页面。

    getStaticProps 是做什么的?

    NextJS 有这个内置的页面预渲染,但是如果你想这样调用它,这个内置过程有一个缺陷。

    以这段代码为例:

        const HomePage = () => {
        const [loadedPosts, setLoadedPosts] = useState();
    
        useEffect(() => {
            setLoadedPosts(DUMMY_POSTS);
        }, []);
    
        return (
            loadedPosts ? (
                <PostList posts={loadedPosts} />
            ) : ''
        )
    }
    

    注意:这是一个传达概念的简单演示。在正常情况下,当您从 useEffect 挂钩中的 API 获取数据时会出现此问题。

    解释:假设我们正在渲染一个帖子列表。 DUMMY_POSTS 是我们的数据,例如来自数据库。

    这里发生的情况是useEffect 挂钩将等待组件呈现,然后再更新loadedPosts

    当 NextJS 预渲染页面时,它会将第一个组件渲染周期的快照作为其内容,这可能会丢失关键数据。这意味着 NextJS 不会在新数据到来后更新我们的代码快照。您知道这对 SEO 不利。

    在客户端接收到这个 HTML 页面后,React 将真正接管,页面在调用此过程时水合,这意味着现在 React 将把它变成一个单页面应用程序,接管控制权,然后执行此useEffect 函数,可能会获取数据并更新页面 - 不是在服务器上,不是在预渲染页面上,而是在浏览器中接收到此页面之后。即没有 SSR。

    这就是getStaticProps() 发挥作用的地方。

    const HomePage = (props) => {
        return (
          <PostList posts={props.posts} />
        )
    }
    
    export function getStaticProps() {
        // fetch data from an API
        return {
            props: {
                posts: DUMMY_POSTS
            },
            revalidate: 10
        };
    };
    
    export default HomePage;
    

    getStaticProps 在构建时运行,它获取数据DUMMY_POSTS,然后将其分配给props。然后将其作为 HomePage 组件的 props 传递,我们可以在其中访问 props.posts

    什么是getStaticPaths

    getStaticPaths 是您需要导出到页面组件文件中的函数,即:

    1. 动态页面和
    2. 正在使用getStaticProps

    不需要在使用getServerProps的文件中需要它,并且如果没有getStaticProps就不需要它

    这里我将使用一个非常简单的示例进行说明,但通常您会像在示例中所做的那样动态生成路径:

    export async function getStaticPaths() {
        return {
          fallback: false,
            paths: [
                { 
                  params: {
                    postdId: 'p1'
                  }
                },
                { 
                  params: {
                    postId: 'p2'
                  }
                },
            ]
        };
    };
    

    在上面的示例中,我们有两个 ID 分别为 p1 和 p2 的帖子。

    getStaticProps 在构建过程中会生成一个页面。这意味着 Next.js 需要为所有支持的动态路径预先生成所有动态页面的所有版本。由于它是动态的 Next.js 需要知道它需要哪些 ID 来预生成页面。这些函数在构建过程中 在客户端访问页面时运行。 IE。如果没有getStaticPaths,这些动态页面将不会在服务器上预先生成,这会破坏 Next.js 的用途。

    getStaticPaths 告诉 Next.js 它需要预先生成的确切动态路径(在我们的例子中是哪些帖子 ID)。如果用户输入了我们没有为其预先生成页面的 ID,他们将看到 404 错误。

    getStaticPaths 返回一个我们描述所有动态段值的对象。它有一个paths 属性,它是一个数组,它为动态页面的每个版本获取一个对象。该对象有一个params 属性,它也是一个对象,它包含所有指向您的动态页面的键值对

    TLDR;

    getStaticProps 在组件加载之前的构建时获取数据,以便 Next.js 可以使用我们正在获取的数据预渲染页面。

    getStaticPaths 告诉 Next.js 需要预渲染的动态页面,因为 Next.js 不会自行预渲染它们,因为它不知道确切的动态 ID(路由)。使用这个函数,你基本上是在告诉 Next.js 你的动态路由是如何被调用的,所以它知道要预渲染什么。否则会显示 404 页面。

    结论: NextJS 在构建时不会生成重复页面。

    编辑:

    关于预生成带有getStaticPaths的页面...

    您不需要预先生成每条动态路线。为了能够加载其他路由而不会出现404错误,可以添加fallback: truefallback: 'blocking'

    如果您有很多页面需要预先生成,这些选项会有所帮助。如果我们有一个像亚马逊这样的网站,其中包含数百万种产品,预渲染所有产品可能需要超长的时间,并且可能有些产品很少被访问。因此,预先生成此类产品是一种资源浪费。

    在这种情况下,我们可以设置fallback: true并预先生成一些页面,而不是全部。

    export async function getStaticPaths() {
    return {
        fallback: true,
        paths: [
            {
                params: {
                    prodId: 'p1'
                }
            }
        ]
    }
    

    }

    即使我们没有列出所有页面,此设置也会告诉 NextJS 其他页面可能是有效的,并且应该在访问时加载。它们及时生成。这允许预生成访问量很大的页面并仅在需要时预生成其他页面。

    但是,如果直接发送带有不支持 ID 的请求(不是在内部通过点击产品),它将返回 404。在这种情况下,我们需要在我们的组件中有一个后备案例。

    const ProductDetailPage = ({ loadedProducts }) => {
    if (!loadedProducts) {
        return <p>Loading...</p>
    }
    
    return (
        <>
            <h1>{loadedProducts.title}</h1>
            <p>{loadedProducts.description}</p>)
        </>
    )
    

    }

    在上面的代码中,用户将看到 Loading 指示器而不是 404 页面,直到数据到来并显示在页面上。

    另一种方法是设置fallback: 'blocking'。在这些情况下,我们不需要在组件中进行回退,但响应时间会更长。

    const ProductDetailPage = ({ loadedProducts }) => {
    
        return (
            <>
                <h1>{loadedProducts.title}</h1>
                <p>{loadedProducts.description}</p>)
            </>
        )
    }
    
    export async function getStaticPaths() {
        return {
            fallback: 'blocking,
            paths: [
                {
                    params: {
                        prodId: 'p1'
                    }
                }
            ]
        }
    }
    

    这取决于你的情况。有时你需要快速展示一些东西 (fallback: true)。有时等待它是值得的,而且您不想向访问者显示不完整的页面 (fallback: 'blocking')。

    Read more about NextJS's Data fetching 在这里。

    【讨论】:

    • 伙伴,非常感谢您提供如此详细的答案。学习和理解了很多新事物。但是,让我总结一下并问您一个问题以更好地理解它。所以假设我有 1000 个帖子。对于所有 1000 个帖子,我是否必须在动态路由中调用 getStaticPaths,还是只需要一个?
    • 您必须在 getStaticPaths 中为您拥有的所有动态路径映射数据(假设它是 DUMMY_POSTS,您将需要它们的 ID),以便 Next.js 提前知道要预渲染的内容。所以在这种情况下,是的,您必须映射所有 1000 个帖子,但在动态路由文件中只完成一次。
    猜你喜欢
    • 2022-01-09
    • 1970-01-01
    • 2019-12-15
    • 2020-10-17
    • 2021-05-11
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多