🏞快速理解 Next.js 中数据获取方式的作用及区别(getInitialProps、getServerSideProps、getStaticProps)

前言

在上一篇文章中,我介绍了 Next.js 的各种渲染方式: 🌁快速理解 Next.js 中各种渲染方式的作用及区别(SSG、SSR、ISR、CSR),还没有看过的同学可以先去了解一下。这篇文章再详细讲讲 Next.js 中各种数据获取方式。

Next.js 中一共有以下四种数据获取的方式,我们按照顺序逐一介绍:

  • getInitialProps
  • getServerSideProps
  • getStaticPaths 与 getStaticProps

getInitialProps

getInitialProps` 能够在页面中进行服务器端渲染,并允许你进行初始数据填充,这意味着在发送页面时已经在服务端中填充了数据。这对于 SEO 特别有用。

getInitialProps 的使用方式如下:

function Page({ stars }) {
  return <div>Next stars: {stars}</div>
}
​
Page.getInitialProps = async (ctx) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}
​
export default Page

分析一下上面的代码,我们在页面组件中添加 getInitialProps 属性,属性值为一个异步函数,我们可以在函数中进行数据获取的操作,然后函数的返回值将获取到的作为 props 传入页面组件中。

在页面组件中使用了 getInitialProps ,也就说明当前的页面时开启的 SSR 的渲染方式。

getInitialProps 返回的数据在服务端渲染时会被序列化,类似于JSON.stringify 的做法。确保从 getInitialProps 返回的对象是一个纯粹的 Object,而不是使用 DateMapSet

执行时机

getInitialProps 的执行时机分为两种情况,都是在运行时执行的:

  • 直接访问页面时getInitialProps 会在服务端执行
  • 通过 next/linknext/router 跳转至某个页面时getInitialProps 会在客户端执行。

注意点

1. getInitialProps 使用的限制

getInitialProps 只能在页面组件中使用,在其他子组件中是不能使用的。

2. getInitialProps 中模块的引入

如果你在 getInitialProps 中使用服务端的模块,请确保正确导入这些模块,否则会降低应用性能。

例如我们在 getInitialProps 引入了 faker 这个库进行一些模拟数据生成的操作,faker 这个库实际上是只想在服务端进行引入的,但是在打包时 webpack 识别到了引入就会将其一块打包在客户端的包中,这肯定是不必要的。因此我们可以使用 arunoda.me/blog/ssr-an… 这篇文章中介绍的三种方法进行处理:

  1. 使用 eval 函数:

    Webpack 无法静态分析 eval 中的内容。所以它不会打包 faker 模块。

    const faker = eval("require('faker')")
    
  2. 使用 Webpack 的 ignore 插件:

    ignore 插件可以用于在 Webpack 打包时忽略某些模块,使用方式如下:

    module.exports = {
      webpack: function (config) {
        config.plugins.push(
          new require('webpack').IgnorePlugin(/faker/)
        )
    ​
        return config
      }
    }
    
    1. 使用 package.json browser 字段:

      {
        ...
        "browser": {
          "faker": false
        }
        ...
      }
      

3. getInitialProps 目前已基本被 getServerSideProps 替代

Next.js 在 9.3版本中引入了getServerSideProps 这个 API。前面提到了 getInitialProps 会根据情况在服务端或者客户端执行,如果我在 getInitialProps 进行了一些 node.js 或者操作了数据库这种纯服务端的操作,当 getInitialProps 在客户端执行时就会报错,除非自己去做兼容处理。

由于执行环境不统一, 使用成本较高,因此在 getServerSideProps 引入后, getInitialProps 就不再被推荐使用了。

接下来我们就讲讲 getServerSideProps 这个 API。

getServerSideProps

如果你从一个页面导出一个名为 getServerSideProps(服务端渲染)的函数,Next.js 将在每次请求时使用 getServerSideProps 返回的数据对这个页面进行预渲染。

getServerSideProps 的使用方式如下:

function Page({ data }) {
  // 渲染数据...
}
​
// 函数会在每次请求时调用
export async function getServerSideProps() {
  // 从外部 API 获取数据
  const res = await fetch(`https://.../data`)
  const data = await res.json()
​
  // 通过 props 向页面传入数据
  return { props: { data } }
}
​
export default Page

使用方式与 getInitialProps 几乎相同,都是一个用于请求数据的异步函数,将请求到的数据作为函数返回值,数据从页面的 props 中传入。

在页面组件中使用了 getServerSideProps ,也就说明当前的页面时开启的 SSR 的渲染方式。

执行时机

getServerSideProps 只会在服务器端运行,从不在客户端上运行。如果页面中使用 getServerSideProps API,则:

  • 当我们直接请求此页面时,getServerSideProps在请求时运行,并且此页面将使用返回的 props 进行预渲染
  • 当我们通过在客户端使用 next/linknext/router 进行页面跳转请求此页面时,Next.js 会向服务端发送 API 请求,服务端会执行getServerSideProps

注意点

1. getServerSideProps 的使用限制

  • getInitialProps 一样, getServerSideProps 只能在页面组件中使用,在其他子组件中是不能使用的。
  • 必须直接导出 getServerSideProps 为一个独立的函数,而不是像 getInitialProps 可以添加为页面组件的属性。

对比

  • getServerSideProps 和 getInitialProps 都只能在页面组件中使用,而不能在子组件中使用
  • getServerSideProps 和 getInitialProps 都可以在服务端执行

getStaticProps 与 getStaticPath

getStaticProps是 Next.js 中非常重要的一个 API ,当在页面组件中使用了 getStaticProps ,说明当前的页面时开启了 SSG 的渲染方式。SSG 能是页面静态化进行渲染,除非重新进行编译或是设置 revalidate 实现 ISR 渲染。

getStaticProps 使用方式如下:

export default function Blog({ posts }) {
  // 渲染文章...
}
•
// 这个函数会在 build 时接收请求
export async function getStaticProps() {
  // 请求 API 获取文章数据
  const res = await fetch('https://.../posts')
  const posts = await res.json()
•
  // 在构建时,Blog 组件可以接收到 posts 这个 props
  return {
    props: {
      posts,
    },
  }
}

使用方式和前面介绍的两个 API 基本一致,这里不赘述,主要讲讲 getStaticPathsgetStaticPaths 使用方式如下:

/ 这个函数在 build 时会调用
export async function getStaticPaths() {
  // 请求 API 获取数据
  const res = await fetch('https://.../posts')
  const posts = await res.json()
​
  // 基于 posts 获取我们想要预渲染的路径
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
​
  // 在 build 时我们只会预渲染 paths 数组中的路径
  // { fallback: false } 意为其他的路由都会返回 404
  return { paths, fallback: false }
}

getStaticPaths 必须与 getStaticProps 共同使用,这个函数可以返回一个包含 pathsfallback 属性的对象,paths 是一个包含了所有动态路由路径的数组,它决定了哪些路径将被预渲染。而 fallback 则是回退的策略,它存在三种情况

  • false:访问任何在 getStaticPaths() 函数中未返回的路径都会响应 404 页面。
  • true:build 时未生成的路径不会返回 404 页面。而是在第一次请求时进行 SSR 并返回生成的 HTML。这个生成的 HTML 会被缓存,也就是说你访问这个路径时,即便对应的数据已经更新了还是会返回原本的页面,直到缓存过期重新生成 HTML。
  • 'blocking''blocking' 策略与设置为 true 时相同,不同点在于设置为 true 时,我们会直接进入访问的路径,并同时执行 getStaticProps ,并且可以通过 router.isFallback 判断getStaticProps 是否已经执行完成,而 'blocking' 策略则会先执行 getStaticProps ,直到执行完成才会进入新的页面。也就是同步与异步的关系。

getStaticProps 可以加快渲染速度以及提升 SEO ,但如果我们的页面时经常改变的,相对比较动态的,那使用 getStaticProps 进行静态渲染就不太合适。反而应该使用 getServerSideProps ,但 getServerSideProps 又会在每次请求时都执行,因此性能会差一些,用户访问的速度会更慢,但相比纯客户端渲染来说还是有 SEO 上的优势的。

运行时机

getStaticProps 始终在服务端上运行,从不在客户端上运行,具体的运行时机如下:

  • 执行 next build 时总会执行
  • 设置 fallback: true ,访问页面后,会在页面渲染时同步在后台运行
  • 设置 fallback: blocking ,在初次渲染时先运行,执行结束后渲染页面
  • 当使用了 revalidate 时会在后台运行
  • 当使用了 revalidate() 函数时会在后台按需运行

既然 getStaticProps 只会在服务端运行,那就代表我们可以在这里执行一些数据库操作或是引入一些 Node.js 的模块,这里不用担心依赖的处理,Next.js 会自动为我们将 getStaticProps 中引入的模块排除在 Webpack 的打包范围内的。

getStaticPaths 则只会在生产构建期间运行,不会在运行时调用。

注意点

1. getStaticPropsgetStaticPaths 的使用限制

getStaticPropsgetStaticPaths 都只能从 页面组件 导出,不能 将其从非页面文件、_app_document_error 中导出。

2. 开发环境中的 getStaticProps

在开发环境(next dev)中,getStaticProps 将在每次请求时调用。

3. 函数处理的限制

getStaticProps 中是无法访问传入请求的,因为请求在传入之前我们就需要生成静态 HTML,并将 HTML 返回,也就是说 getStaticProps 是在请求进入前执行的。

总结

这篇文章介绍了 Next.js 中官方提供的所有数据获取方式,这些数据获取方式看似相似,其实在理解了对应的渲染机制后还是比较好理解的。掌握了这些 API 就能理解 Next.js 与传统 CSR 渲染之间的区别了。

如果文章对你有帮助,除了收藏外不妨点个赞支持下作者,respect!

本文正在参加「金石计划」

原文链接:https://juejin.cn/post/7214831158873571383 作者:oil欧哟

(4)
上一篇 2023年3月27日 上午11:20
下一篇 2023年3月27日 上午11:31

相关推荐

发表回复

登录后才能评论