Next.js教程基本特性:获取数据

本文档适用于9.3及更高版本的Next.js。如果您使用的是Next.js的旧版本,请参阅我们以前的文档。

例子

在Pages文档中,我们解释了Next.js具有两种形式的预渲染:静态生成服务器端渲染。在此页面中,我们将深入讨论每种情况下的数据获取策略。

我们将讨论可用于获取数据以进行预渲染的三个独特的Next.js函数:

另外,我们将简要讨论如何在客户端获取数据。

getStaticProps (静态生成)

版本记录

如果导出从页面async调用的函数getStaticProps,则Next.js将在构建时使用所返回的道具来预渲染此页面getStaticProps

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

context参数是一个包含以下键的对象:

  • params包含使用动态路由的页面的路由参数。例如,如果页面名称是[id].jsparams则将看起来像{ id: ... }。要了解更多信息,请查看动态路由文档。您应该将其与一起使用getStaticPaths,我们将在后面说明。
  • previewtrue,如果该页面在预览模式和undefined其他。请参阅预览模式文档
  • previewData包含设置的预览数据setPreviewData。请参阅预览模式文档
  • locale 包含活动的语言环境(如果启用)。
  • locales 包含所有支持的语言环境(如果启用)。
  • defaultLocale 包含配置的默认语言环境(如果启用)。

getStaticProps 应该返回一个对象:

  • props-页面组件将接收到的带有道具的必需对象。它应该是一个可序列化的对象
  • revalidate可选的时间(以秒为单位),之后可以重新生成页面。有关增量静态再生的更多信息
  • notFound-一个可选的布尔值,允许页面返回404状态和页面。以下是其工作方式的示例:export async function getStaticProps(context) { const res = await fetch(`https://.../data`) const data = await res.json() if (!data) { return { notFound: true, } } return { props: {}, // will be passed to the page component as props } } 注意:模式notFound不需要,fallback: false因为只会getStaticPaths预渲染从返回的路径。
  • redirect-一个可选的重定向值,允许重定向到内部和外部资源。它应与的形状匹配{ destination: string, permanent: boolean }。在极少数情况下,您可能需要为旧的HTTP客户端分配自定义状态代码,以正确重定向。在这些情况下,您可以使用statusCode属性代替permanent属性,但不能同时使用两者。以下是其工作方式的示例:export async function getStaticProps(context) { const res = await fetch(`https://...`) const data = await res.json() if (!data) { return { redirect: { destination: '/', permanent: false, }, } } return { props: {}, // will be passed to the page component as props } } 注意:目前不允许在构建时进行重定向,如果在构建时已知重定向,则应将其添加到中next.config.js

注意:您可以导入顶级范围的模块以在中使用getStaticProps。采用进口getStaticProps将不会被捆绑的客户端。

这意味着您可以直接在中getStaticProps编写服务器端代码。这包括从文件系统或数据库中读取。

注意:请勿用于fetch()在中调用API路由getStaticProps。而是直接导入API路由内使用的逻辑。您可能需要针对这种方法稍微重构代码。

从外部API提取是可以的!

简单的例子

这是一个示例,用于getStaticProps从CMS(内容管理系统)中获取博客文章列表。Pages文档中也有此示例。

// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // By returning { props: posts }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog

什么时候应该使用getStaticProps

您应在以下情况下使用getStaticProps

  • 呈现页面所需的数据可在构建时在用户请求之前获得。
  • 数据来自无头CMS。
  • 数据可以被公共缓存(不是特定于用户的)。
  • 该页面必须预渲染(对于SEO)并且必须非常快-getStaticProps生成HTML和JSON文件,CDN可以将它们都缓存以提高性能。

TypeScript:使用 GetStaticProps

对于TypeScript,您可以使用以下GetStaticProps类型next

import { GetStaticProps } from 'next'

export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}

如果您想推断道具的类型,可以使用InferGetStaticPropsType<typeof getStaticProps>,如下所示:

import { InferGetStaticPropsType } from 'next'

type Post = {
  author: string
  content: string
}

export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()

  return {
    props: {
      posts,
    },
  }
}

function Blog({ posts }: InferGetStaticPropsType) {
  // will resolve posts to type Post[]
}

export default Blog

增量式静态再生

Next.js 9.5及更高版本中引入了此功能。如果您使用的是Next.js的旧版本,请先进行升级,然后再尝试增量静态再生。

例子

  • 静态反应演示

有了getStaticProps您,您不必停止依赖动态内容,因为静态内容也可以是dynamic。增量静态重新生成使您可以通过在访问量增加时在后台重新渲染现有页面来更新现有页面。

受过时验证的启发,后台重新生成可确保始终从静态存储中无间断地为流量提供服务,并且仅在完成生成后才推送新建页面。

考虑我们前面的getStaticProps示例,但是现在启用了再生:

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every second
    revalidate: 1, // In seconds
  }
}

export default Blog

现在,博客帖子列表将每秒重新验证一次;如果您添加新的博客文章,则几乎立即可用,而无需重新构建您的应用程序或进行新的部署。

这与完美搭配fallback: true。因为现在您可以拥有始终与最新帖子保持最新的帖子列表,并且拥有一个博客帖子页面,无论您添加或更新多少个帖子,该页面都会按需生成博客帖子。

大规模静态内容

与传统的SSR不同,增量静态再生可确保您保留静态的好处:

  • 延迟没有峰值。页面持续快速投放
  • 页面永远不会脱机。如果后台页面重新生成失败,则旧页面保持不变
  • 低数据库和后端负载。页面最多同时重新计算一次

读取文件:使用 process.cwd()

可以从中直接从文件系统读取文件getStaticProps

为此,您必须获取文件的完整路径。

由于Next.js将您的代码编译到一个单独的目录中,因此您无法使用__dirname它,因为它将返回的路径与pages目录不同。

相反,您可以使用process.cwd()它为您提供执行Next.js的目录。

import { promises as fs } from 'fs'
import path from 'path'

// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>
          <h3>{post.filename}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), 'posts')
  const filenames = await fs.readdir(postsDirectory)

  const posts = filenames.map(async (filename) => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = await fs.readFile(filePath, 'utf8')

    // Generally you would parse/transform the contents
    // For example you can transform markdown to HTML here

    return {
      filename,
      content: fileContents,
    }
  })
  // By returning { props: posts }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts: await Promise.all(posts),
    },
  }
}

export default Blog

技术细节

仅在构建时运行

因为getStaticProps它在生成时运行,所以它不会接收仅在请求时间内可用的数据,例如查询参数或HTTP标头,因为它会生成静态HTML。

直接编写服务器端代码

请注意,getStaticProps它仅在服务器端运行。它永远不会在客户端上运行。它甚至不会包含在浏览器的JS包中。这意味着您可以编写代码,例如直接数据库查询,而无需将其发送到浏览器。您不应该从中获取API路由getStaticProps而是可以直接在中编写服务器端代码getStaticProps

您可以使用此工具来验证Next.js从客户端捆绑软件中消除了什么。

静态生成HTML和JSON

getStaticProps在构建时预渲染带有的页面时,除了页面HTML文件外,Next.js还会生成一个JSON文件,其中包含运行结果getStaticProps

此JSON文件将用于next/link(文档)或next/router(文档)的客户端路由中。当您导航到使用预先渲染的页面时getStaticProps,Next.js会获取此JSON文件(在构建时预先计算),并将其用作页面组件的道具。这意味着将不会调用客户端页面转换,getStaticProps因为仅使用导出的JSON。

仅在页面中允许

getStaticProps只能从页面导出。您不能从非页面文件中导出它。

出现此限制的原因之一是,在呈现页面之前,React需要拥有所有必需的数据。

此外,您必须使用export async function getStaticProps() {}-它会不会,如果你添加工作getStaticProps作为网页组件的属性。

在开发中的每个请求上运行

在开发(next dev)中,getStaticProps将在每个请求上调用。

预览模式

在某些情况下,您可能希望暂时绕过Static Generation,并在请求时而不是在构建时呈现页面。例如,您可能正在使用无头CMS,并且想要在草稿发布之前对其进行预览。

Next.js通过称为“预览模式”的功能支持此用例。在“预览模式”文档中了解更多信息

getStaticPaths (静态生成)

版本记录

如果页面具有动态路由(文档)并使用getStaticProps它,则需要定义一个在构建时必须呈现为HTML的路径列表。

如果从使用动态路由的页面导出一个async调用的函数getStaticPaths,则Next.js将静态预呈现由指定的所有路径getStaticPaths

export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // See the "paths" section below
    ],
    fallback: true or false // See the "fallback" section below
  };
}

pathskey(必填)

paths键确定哪些路径将被预呈现。例如,假设您有一个页面,该页面使用名为的动态路由pages/posts/[id].js。如果getStaticPaths从此页面导出并返回以下内容paths

return {
  paths: [
    { params: { id: '1' } },
    { params: { id: '2' } }
  ],
  fallback: ...
}

然后,Next.js将使用中的页面组件静态生成posts/1posts/2在构建时生成pages/posts/[id].js

请注意,每个params参数的值都必须与页面名称中使用的参数匹配:

  • 如果页面名称为pages/posts/[postId]/[commentId]params则应包含postIdcommentId
  • 如果页面名称使用包罗万象的路由,例如pages/[...slug]params则应包含slug是一个数组。例如,如果此数组为['foo', 'bar'],则Next.js将在静态生成页面/foo/bar
  • 如果该页面使用一个可选的包罗万象的路线,供应null[]undefinedfalse呈现根最航线。例如,如果你提供slug: falsepages/[[...slug]],Next.js会生成静态页面/

fallbackkey(必填)

返回的对象getStaticPaths必须包含一个布尔fallback键。

fallback: false

如果fallbackfalse,则所有未返回的路径getStaticPaths都将产生404页面。如果您有少量要预渲染的路径,则可以执行此操作-因此它们都是在构建时静态生成的。当不经常添加新页面时,它也很有用。如果您向数据源添加更多项并需要呈现新页面,则需要再次运行该构建。

这是一个示例,该示例每页预渲染一个博客帖子,称为pages/posts/[id].js。博客文章列表将从CMS中获取,并由返回getStaticPaths。然后,对于每个页面,它使用来从CMS中获取帖子数据getStaticPropsPages文档中也有此示例。

// pages/posts/[id].js

function Post({ post }) {
  // Render post...
}

// This function gets called at build time
export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return { props: { post } }
}

export default Post

fallback: true

例子

如果fallbacktrue,则getStaticProps更改行为:

  • 从返回的路径getStaticPaths将在构建时通过渲染为HTML getStaticProps
  • 在生成时尚未生成的路径将不会产生404页面。相反,Next.js会在对此类路径的第一个请求时提供页面的“后备”版本(有关详细信息,请参见下面的“后备页面”)。
  • 在后台,Next.js将静态生成请求的路径HTML和JSON。这包括跑步getStaticProps
  • 完成后,浏览器将接收所生成路径的JSON。这将用于自动呈现带有所需道具的页面。从用户的角度来看,该页面将从后备页面切换到整个页面。
  • 同时,Next.js将此路径添加到预渲染页面列表中。对相同路径的后续请求将服务于生成的页面,就像在构建时预渲染的其他页面一样。

fallback: true使用时不支持next export

后备页面

在页面的“后备”版本中:

  • 该页面的道具将为空。
  • 使用路由器,你可以检测后备正在呈现,router.isFallback将是true

这是一个使用的示例isFallback

// pages/posts/[id].js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // If the page is not yet generated, this will be displayed
  // initially until getStaticProps() finishes running
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render post...
}

// This function gets called at build time
export async function getStaticPaths() {
  return {
    // Only `/posts/1` and `/posts/2` are generated at build time
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // Enable statically generating additional pages
    // For example: `/posts/3`
    fallback: true,
  }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return {
    props: { post },
    // Re-generate the post at most once per second
    // if a request comes in
    revalidate: 1,
  }
}

export default Post

什么时候fallback: true有用?

fallback: true如果您的应用程序具有大量依赖于数据的静态页面(例如:非常大的电子商务网站),则此功能非常有用。您想预渲染所有产品页面,但是构建将永远耗时。

取而代之的是,您可以静态生成页面的一小部分,并fallback: true用于其余部分。当有人请求尚未生成的页面时,用户将看到带有加载指示器的页面。不久之后,getStaticProps完成操作,页面将使用请求的数据呈现。从现在开始,请求同一页面的每个人都将获得静态预渲染的页面。

这样可以确保用户在保持快速构建的同时始终获得快速体验,并获得“静态生成”的好处。

fallback: true不会更新生成的页面,为此请看一下“增量静态再生”

fallback: 'blocking'

如果fallback'blocking',则未返回的新路径getStaticPaths将等待生成与SSR相同的HTML(因此为何阻塞),然后将其缓存以供将来的请求使用,因此每个路径仅发生一次。

getStaticProps 表现如下:

  • 从返回的路径getStaticPaths将在构建时通过渲染为HTML getStaticProps
  • 在生成时尚未生成的路径将不会产生404页面。相反,Next.js将对第一个请求进行SSR并返回生成的HTML。
  • 完成后,浏览器将接收所生成路径的HTML。从用户的角度来看,它将从“浏览器正在请求页面”过渡到“已加载整个页面”。没有加载/后备状态的闪烁。
  • 同时,Next.js将此路径添加到预渲染页面列表中。对相同路径的后续请求将服务于生成的页面,就像在构建时预渲染的其他页面一样。

fallback: 'blocking'默认情况下不会更新生成的页面。要更新生成的页面,请结合使用增量静态再生fallback: 'blocking'

fallback: 'blocking'使用时不支持next export

getStaticPaths什么时候应该使用?

getStaticPaths如果您要静态地预渲染使用动态路由的页面,则应该使用。

TypeScript:使用 GetStaticPaths

对于TypeScript,您可以使用以下GetStaticPaths类型next

import { GetStaticPaths } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

技术细节

getStaticProps一起使用

getStaticProps带有动态路由参数的页面上使用时,必须使用getStaticPaths

您不能getStaticPaths与一起使用getServerSideProps

仅在服务器端在构建时运行

getStaticPaths 仅在构建时在服务器端运行。

仅在页面中允许

getStaticPaths只能从页面导出。您不能从非页面文件中导出它。

此外,您必须使用export async function getStaticPaths() {}-它会不会,如果你添加工作getStaticPaths作为网页组件的属性。

在开发中的每个请求上运行

在开发(next dev)中,getStaticPaths将在每个请求上调用。

getServerSideProps (服务器端渲染)

版本记录

如果导出从页面async调用的函数getServerSideProps,则Next.js将使用所返回的数据在每个请求上预呈现此页面getServerSideProps

export async function getServerSideProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

context参数是一个包含以下键的对象:

  • params:如果此页面使用动态路由,则params包含路由参数。如果页面名称是[id].jsparams则将看起来像{ id: ... }。要了解更多信息,请查看动态路由文档
  • req:HTTP IncomingMessage对象。
  • res:HTTP响应对象。
  • query:代表查询字符串的对象。
  • previewpreview表示true页面是否处于预览模式,false否则。请参阅预览模式文档
  • previewData:设置的预览数据setPreviewData。请参阅预览模式文档
  • resolvedUrl:请求网址的规范化版本,该版本会剥离_next/data客户端转换的前缀并包含原始查询值。
  • locale 包含活动的语言环境(如果启用)。
  • locales 包含所有支持的语言环境(如果启用)。
  • defaultLocale 包含配置的默认语言环境(如果启用)。

getServerSideProps 应该返回一个对象:

  • props-页面组件将接收到的带有道具的必需对象。它应该是一个可序列化的对象
  • notFound-一个可选的布尔值,允许页面返回404状态和页面。以下是其工作方式的示例:export async function getServerSideProps(context) { const res = await fetch(`https://...`) const data = await res.json() if (!data) { return { notFound: true, } } return { props: {}, // will be passed to the page component as props } }
  • redirect-一个可选的重定向值,允许重定向到内部和外部资源。它应与的形状匹配{ destination: string, permanent: boolean }。在极少数情况下,您可能需要为旧的HTTP客户端分配自定义状态代码,以正确重定向。在这些情况下,您可以使用statusCode属性代替permanent属性,但不能同时使用两者。以下是其工作方式的示例:export async function getServerSideProps(context) { const res = await fetch(`https://.../data`) const data = await res.json() if (!data) { return { redirect: { destination: '/', permanent: false, }, } } return { props: {}, // will be passed to the page component as props } }

注意:您可以导入顶级范围的模块以在中使用getServerSideProps。在中使用的导入getServerSideProps不会在客户端捆绑在一起。

这意味着您可以直接在中getServerSideProps编写服务器端代码。这包括从文件系统或数据库中读取。

注意:请勿用于fetch()在中调用API路由getServerSideProps。而是直接导入API路由内使用的逻辑。您可能需要针对这种方法稍微重构代码。

从外部API提取是可以的!

简单的例子

这是一个示例,该示例用于getServerSideProps在请求时获取数据并将其预呈现。Pages文档中也有此示例。

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

getServerSideProps什么时候应该使用?

getServerSideProps仅当需要预渲染其数据必须在请求时获取的页面时,才应使用。到第一个字节(TTFB)的时间将比getStaticProps由于服务器必须在每个请求上计算结果而花费的时间要慢,并且如果没有额外的配置,结果就不能由CDN缓存。

TypeScript:使用 GetServerSideProps

对于TypeScript,您可以使用以下GetServerSideProps类型next

import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}

如果您想推断道具的类型,可以使用InferGetServerSidePropsType<typeof getServerSideProps>,如下所示:

import { InferGetServerSidePropsType } from 'next'

type Data = { ... }

export const getServerSideProps = async () => {
  const res = await fetch('https://.../data')
  const data: Data = await res.json()

  return {
    props: {
      data,
    },
  }
}

function Page({ data }: InferGetServerSidePropsType) {
  // will resolve posts to type Data
}

export default Page

技术细节

仅在服务器端运行

getServerSideProps仅在服务器端运行,而从未在浏览器上运行。如果页面使用getServerSideProps,则:

  • 当您直接请求此页面时,getServerSideProps将在请求时间运行,并且该页面将使用返回的道具进行预渲染。
  • 当您通过next/link(documentation)或next/router(documentation)在客户端页面转换上请求此页面时,Next.js将API请求发送到服务器,该服务器运行getServerSideProps。它将返回包含running的结果getServerSideProps的JSON,并且该JSON将用于呈现页面。所有这些工作将由Next.js自动处理,因此您只要getServerSideProps定义就不需要做任何额外的事情。

您可以使用此工具来验证Next.js从客户端捆绑软件中消除了什么。

仅在页面中允许

getServerSideProps只能从页面导出。您不能从非页面文件中导出它。

此外,您必须使用export async function getServerSideProps() {}-它会不会,如果你添加工作getServerSideProps作为网页组件的属性。

在客户端获取数据

如果您的页面包含经常更新的数据,并且您不需要预先呈现数据,则可以在客户端获取数据。一个示例是用户特定的数据。运作方式如下:

  • 首先,立即显示没有数据的页面。可以使用“静态生成”来预渲染页面的某些部分。您可以显示丢失数据的加载状态。
  • 然后,在客户端获取数据,并在准备好时显示它们。

例如,此方法适用于用户仪表板页面。由于仪表板是一个私有的,特定于用户的页面,因此SEO不相关,并且该页面不需要预先呈现。数据经常更新,这需要获取请求时的数据。

SWR

Next.js背后的团队创建了一个名为SWR的React挂钩来进行数据获取。如果您要在客户端获取数据,我们强烈建议您这样做。它处理缓存,重新验证,焦点跟踪,间隔重新获取等等。您可以像这样使用它:

import useSWR from 'swr'

function Profile() {
  const { data, error } = useSWR('/api/user', fetch)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

作者:terry,如若转载,请注明出处:https://www.web176.com/nextjs/2481.html

(1)
打赏 支付宝 支付宝 微信 微信
terryterry
上一篇 2021年4月21日 上午11:34
下一篇 2021年4月21日 下午1:41

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注