MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Next.js 数据获取策略:getStaticProps 与 getServerSideProps 的对比

2024-02-074.9k 阅读

Next.js 数据获取策略概述

在 Next.js 应用开发中,数据获取是一个关键环节。Next.js 提供了两种主要的数据获取策略:getStaticPropsgetServerSideProps。这两种策略适用于不同的场景,理解它们的差异对于构建高性能、动态性强的 Next.js 应用至关重要。

Next.js 的渲染模式

在深入了解这两种数据获取策略之前,先简要回顾一下 Next.js 的渲染模式。Next.js 支持三种主要的渲染模式:

  1. 静态生成(Static Generation):在构建时生成 HTML 页面。这种模式适用于内容不经常变化的页面,如博客文章、产品介绍等。getStaticProps 就属于静态生成的一部分。
  2. 服务器端渲染(Server - Side Rendering,SSR):在每次请求时在服务器端生成 HTML 页面。这种模式适用于需要实时数据的页面,如个性化的用户仪表板、实时股票价格页面等。getServerSideProps 是服务器端渲染的关键函数。
  3. 客户端渲染(Client - Side Rendering,CSR):在浏览器中渲染页面。这种模式下,初始 HTML 可能只包含一个空的容器,页面内容通过 JavaScript 在客户端获取并渲染。虽然 Next.js 主要关注静态生成和服务器端渲染,但客户端渲染在某些情况下也会用到。

getStaticProps

基本概念

getStaticProps 是 Next.js 提供的一个函数,用于在构建时获取数据并将其作为 props 传递给页面组件。这意味着页面的 HTML 以及其中的数据在构建阶段就已经确定,不需要在每次请求时重新获取。

适用场景

  1. 内容驱动的页面:对于像博客文章、产品页面等内容相对固定的页面,getStaticProps 是理想的选择。例如,一个博客网站的文章页面,文章内容可能几个月甚至几年才会更新一次,使用 getStaticProps 可以在构建时就将文章内容渲染到 HTML 中,提高页面加载速度。
  2. SEO 友好的页面:由于页面在构建时就已经生成,搜索引擎爬虫可以轻松地抓取到完整的页面内容,这对于搜索引擎优化(SEO)非常有利。

代码示例

假设我们有一个博客应用,其中文章存储在 Markdown 文件中。首先,安装 gray - matter 库来处理 Markdown 元数据,以及 markdown - to - html 库将 Markdown 转换为 HTML。

npm install gray - matter markdown - to - html

然后,创建一个 pages/post/[slug].js 文件,用于显示单个博客文章:

import React from'react';
import matter from 'gray - matter';
import { markdownToHtml } from'markdown - to - html';
import Layout from '../../components/layout';

const Post = ({ post }) => {
    return (
        <Layout>
            <h1>{post.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.content }} />
        </Layout>
    );
};

export async function getStaticProps({ params }) {
    const slug = params.slug;
    const markdownWithMeta = await import(`../../posts/${slug}.md`);
    const { data, content } = matter(markdownWithMeta.default);
    const htmlContent = await markdownToHtml(content);
    return {
        props: {
            post: {
               ...data,
                content: htmlContent
            }
        },
        revalidate: 60 * 60 * 24 // 一天重新验证一次
    };
}

export async function getStaticPaths() {
    const files = require.context('../../posts', false, /\.md$/);
    const slugs = files.keys().map((key) => key.replace(/^.+\//, '').replace(/\.md$/, ''));
    return {
        paths: slugs.map((slug) => ({ params: { slug } })),
        fallback: false
    };
}

export default Post;

在上述代码中,getStaticProps 函数在构建时运行。它首先根据页面参数 params.slug 导入对应的 Markdown 文件,然后使用 gray - matter 解析 Markdown 元数据和内容,再将 Markdown 内容转换为 HTML。getStaticPaths 函数用于生成所有可能的页面路径,这里是所有博客文章的路径。

增量静态再生

从 Next.js 9.5 开始,getStaticProps 支持增量静态再生(Incremental Static Regeneration)。通过设置 revalidate 选项,可以指定在页面构建后每隔一段时间重新验证数据。例如:

export async function getStaticProps() {
    const data = await fetchData();
    return {
        props: {
            data
        },
        revalidate: 60 // 每分钟重新验证一次
    };
}

当启用增量静态再生时,在重新验证时间间隔内,如果有新请求,Next.js 会使用旧的缓存数据进行响应。同时,在后台会尝试重新生成页面数据。一旦新数据生成完成,后续请求将使用新的数据。

getServerSideProps

基本概念

getServerSideProps 是 Next.js 用于服务器端渲染(SSR)的数据获取函数。与 getStaticProps 在构建时获取数据不同,getServerSideProps 在每次请求页面时在服务器端运行,从而确保返回给客户端的是最新的数据。

适用场景

  1. 实时数据页面:对于需要实时数据的页面,如显示实时股票价格、实时天气信息、用户个性化仪表板等,getServerSideProps 是首选。因为这些数据可能每秒都在变化,静态生成无法满足实时性需求。
  2. 基于用户会话的页面:如果页面内容依赖于用户的登录状态、用户偏好等会话数据,getServerSideProps 可以在请求时从服务器会话中获取这些信息,并根据用户特定的数据渲染页面。

代码示例

假设我们有一个显示用户个性化信息的页面。首先,创建一个 pages/dashboard.js 文件:

import React from'react';
import Layout from '../../components/layout';

const Dashboard = ({ userData }) => {
    return (
        <Layout>
            <h1>Welcome, {userData.name}</h1>
            <p>Your email: {userData.email}</p>
        </Layout>
    );
};

export async function getServerSideProps(context) {
    // 模拟从服务器会话中获取用户数据
    const userData = await getUserDataFromSession(context.req);
    return {
        props: {
            userData
        }
    };
}

function getUserDataFromSession(req) {
    // 实际应用中,这里会从会话存储(如 cookie - based session 或 JWT)中获取用户数据
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({
                name: 'John Doe',
                email: 'john@example.com'
            });
        }, 1000);
    });
}

export default Dashboard;

在上述代码中,getServerSideProps 函数在每次请求 dashboard 页面时运行。它通过 context.req 对象获取请求信息,并从模拟的服务器会话中获取用户数据。然后将用户数据作为 props 传递给 Dashboard 组件。

性能考虑

虽然 getServerSideProps 可以提供实时数据,但由于它在每次请求时都在服务器端运行,会增加服务器的负载。特别是在高流量的应用中,频繁的服务器端数据获取可能导致性能问题。因此,在使用 getServerSideProps 时,需要仔细评估数据的实时性需求和服务器的承载能力。可以考虑采用缓存策略来减少不必要的数据获取,例如在服务器端缓存数据一段时间,对于短时间内重复的请求直接返回缓存数据。

getStaticProps 与 getServerSideProps 的对比

数据获取时机

  1. getStaticProps:在构建时获取数据。这意味着页面的 HTML 和数据在构建阶段就已经确定,后续请求直接使用构建好的文件,除非启用了增量静态再生。这种方式适用于数据不经常变化的场景,能够极大地提高页面加载速度,因为不需要每次请求都进行数据获取和渲染。
  2. getServerSideProps:在每次请求时获取数据。无论页面是否已经被缓存,每次请求都会触发 getServerSideProps 函数在服务器端运行,确保返回给客户端的是最新的数据。这对于实时性要求极高的数据非常有用,但会增加服务器的负载。

适用场景差异

  1. 内容驱动与实时数据:如前文所述,getStaticProps 适合内容驱动的页面,如博客文章、产品介绍等,这些页面的内容更新频率较低。而 getServerSideProps 适用于需要实时数据的场景,如金融数据展示、实时监控面板等。
  2. SEO 与用户个性化getStaticProps 生成的静态页面对于 SEO 非常友好,因为搜索引擎爬虫可以直接抓取到完整的页面内容。而 getServerSideProps 更适合需要根据用户会话数据进行个性化渲染的页面,如用户个人资料页面、个性化推荐页面等。

性能和成本

  1. 性能getStaticProps 由于在构建时生成页面,不需要每次请求都进行数据获取和渲染,性能通常较好,尤其是对于大量用户同时访问的页面。而 getServerSideProps 每次请求都在服务器端运行,会带来一定的性能开销,特别是在高流量情况下。
  2. 成本:从服务器成本角度看,getStaticProps 构建后的静态文件可以轻松地部署到 CDN,减少服务器的负载,成本相对较低。getServerSideProps 由于每次请求都需要服务器进行数据获取和渲染,对服务器资源的消耗较大,可能导致更高的成本,尤其是在高并发场景下。

缓存和更新策略

  1. getStaticProps:可以通过增量静态再生(设置 revalidate 选项)来实现缓存更新。在重新验证时间间隔内,页面使用旧的缓存数据,同时后台尝试重新生成新数据。这种策略在保证一定实时性的同时,也能利用缓存提高性能。
  2. getServerSideProps:由于每次请求都获取最新数据,不存在类似的缓存机制。不过,开发者可以在服务器端手动实现缓存逻辑,例如使用内存缓存(如 node - cache 库),在一定时间内缓存数据,减少重复的数据获取。

代码复杂度

  1. getStaticProps:代码相对简单,主要关注构建时的数据获取逻辑。由于数据获取在构建阶段完成,不需要处理请求相关的复杂逻辑,如请求头、会话管理等。
  2. getServerSideProps:代码相对复杂,因为它需要在每次请求时运行,要处理请求上下文(如 context.reqcontext.res),可能涉及到会话管理、身份验证等复杂逻辑。例如,在获取用户个性化数据时,需要从请求头中验证用户身份,从会话存储中获取用户信息等。

选择合适的数据获取策略

在实际开发中,选择 getStaticProps 还是 getServerSideProps 取决于具体的业务需求。

  1. 如果页面内容不经常变化且对 SEO 有较高要求:优先选择 getStaticProps。例如,一个公司的关于我们页面、常见问题页面等,这些页面内容可能几个月甚至几年才会更新一次,使用 getStaticProps 可以在构建时生成静态页面,提高页面加载速度和 SEO 效果。
  2. 如果页面需要实时数据或依赖用户会话:则选择 getServerSideProps。比如一个在线游戏的实时排行榜页面、电商应用中用户的购物车页面等,这些页面的数据需要实时更新或者依赖用户的登录状态,getServerSideProps 能够满足这些需求。
  3. 混合使用策略:在一些复杂的应用中,可能会混合使用这两种策略。例如,一个博客应用的文章列表页面可以使用 getStaticProps 进行静态生成,以提高加载速度和 SEO 效果。而文章的评论部分,由于需要实时显示最新评论,可以在客户端使用 fetch 或者在服务器端使用 getServerSideProps 来获取实时数据。

此外,还需要考虑项目的性能需求、服务器成本以及开发和维护的复杂度。通过合理选择和使用 getStaticPropsgetServerSideProps,可以构建出高性能、动态性强且用户体验良好的 Next.js 应用。在开发过程中,不断测试和优化数据获取策略,以确保应用在不同场景下都能达到最佳性能。同时,随着业务需求的变化,可能需要灵活调整数据获取策略,例如从静态生成切换到服务器端渲染,或者反之。总之,深入理解这两种数据获取策略的差异,并根据实际情况做出明智的选择,是 Next.js 应用开发成功的关键之一。