在Next.js项目中获取动态数据的3种方法及对比

摘要

Next.js是一个面向生产环境的React框架,其零配置和API路由的特性使开发者可以专注于应用的页面、组件和逻辑;而SSG和SSR混合模式以及强大的增量静态生成特性又使应用从出生就可以拥有优化过的加载速度和SEO。因此Next.js框架十分适合诸如网站首页、博客、新闻、电商等类型应用的开发。

除了纯静态首页以外,其余场景都涉及动态获取数据的需求,比如文章、新闻、商品信息等,那么如何在不牺牲用户体验和SEO的前提下更新数据就是每个Next.js开发者需要面对的问题。

官方文档的Data Fetching章节给出了Next.js提供的数据获取方法。本文将从实际需求出发介绍使用Next.js获取动态数据的3种方法,并分析各方法适用的场景,来看看如何利用Next.js实现最大化的“动静结合”。因为Next.js的官方文档写得已经足够好了,所以本文不会覆盖Data Fetching以外的内容,强烈建议初学者至少阅读过Pages和Data Fetching后再阅读本文。

方法1:服务端渲染(SSR)

服务端渲染是指用户请求一个页面后,服务器先进行动态数据的获取然后生成页面数据返回给前端显示,这与单页应用最常见的先渲染页面再用ajax请求更新局部的方式是相反的。其优势在于对爬虫十分友好,利于SEO,而且服务端处理请求的速度往往比用户端快得多,最终页面能够更快呈现给用户。

Next.js提供了getServerSideProps方法来实现基于SSR的数据动态更新,其基本用法如下:

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方法返回的props属性会作为页面组件的初始props传入,这样页面每次渲染就都是最新的数据了。

要注意的是SSR的方式并不会减少请求数量,与前端ajax渲染一样,仍是页面每次刷新都会发送动态数据的请求。因此SSR更适合对数据实时性有很高要求的场景,比如实时数据统计、实时比分等。那如果我只是个月更作者,一个月才更新一篇文章呢?使用SSR岂不是白白消耗服务器性能?

方法2:构建时预渲染(SSG)

SSG是发生在构建时的,即Next.js应用会在构建时获取一次动态数据,之后基于此数据生成静态页面文件。在运行时应用就是完全静态了。而我之所以要把这个运行时完全静态的方案也归为动态获取数据的方法,是与Next.js的部署方案有关。Next.js的作者自家的Vercel是部署Next.js应用的绝佳平台,也是很多Next.js应用会选择的部署平台,它支持git提交直接触发部署,很多其他云服务提供商也开始支持类似的Next.js应用部署方式。部署过程对于用户端是无感的,因此对于用户端来说其实就是运行时动态更新的体验,完全适合前文月更作者的需求。

Next.js提供了getStaticProps方法来实现页面的SSG,其基本用法如下:

// 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
复制代码

其用法与getServerSideProps基本一致,只是内部运行机制不同。

那么如果既不想浪费服务器性能,又对实时性有一定要求该怎么办呢?也不能每隔几小时或者几分钟就部署一次吧?

方法3:增量静态生成(Incremental Static Regeneration)

Next.js最强特性来了。增量静态生成是在构建时预渲染的基础上,支持为页面设置过期检测时间间隔,在一个间隔内最多只会获取一次最新数据,如果数据有变化会生成新的静态页面文件,而如果新数据导致页面生成失败也不用担心,Next.js会保留老页面的缓存,这样就实现了动态的静态文件生成。保留了静态文件优势的同时兼顾了时效性。

增量静态生成特性的开启方式十分简单,只需在getStaticProps的返回对象中增加一个revalidate字段即可,用来表示过期检测间隔。不过由于可能会有新增或失效的路由,所以还应该添加getStaticPaths来告诉Next.js应该预渲染哪些路由的页面,代码示例如下:

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 10 seconds
    revalidate: 10, // In seconds
  }
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
  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: blocking } will server-render pages
  // on-demand if the path doesn't exist.
  return { paths, fallback: 'blocking' }
}

export default Blog
复制代码

其中revalidate: 10代表最少间隔10秒会更新一次数据。要注意这个10秒与定时器setInterval不是一个概念,它其实是对数据更新请求的节流,只有上一请求10秒后的第一个页面请求会触发更新,如果没有请求就不会更新了,所以如果网站访问量少的话,这一方法几乎与纯静态文件服务一样节约服务器性能。但也因此数据的更新会最多滞后10秒,revalidate设置得越大,平均滞后时间就会越长!

总结

本文介绍了Next.js提供的三种数据动态获取方法,分别是服务端渲染(SSR)、构建时预渲染(SSG)和增量静态生成(ISR),三种方案都能保证Next.js的页面加载速度和SEO优势。其中SSR实时性最高但也最耗性能;SSG对服务器压力最小但更新数据时也相对没那么灵活;ISR则可以通过过期检测间隔在前两者间寻找适合应用的平衡点。

综上所述,ISR是能适应大多数应用场景的方法,但也要记住数据获取只是页面级的逻辑,我们完全可以针对不同页面灵活使用这3种方法,宗旨就是SSG越多越好,对数据实时性要求极高的用SSR,其余的用ISR。这样无论什么应用,都能最大化体现Next.js带来的优势。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享