在Next.js SSR/ISR中,fallback false与true与阻塞getStaticPaths(有和没有重新验证)之间有什么区别?

z31licg0  于 2022-11-23  发布在  其他
关注(0)|答案(3)|浏览(152)

从Next.js 10开始,getStaticPaths函数返回一个对象,该对象必须包含非常重要的fallback键,如以下位置所示:https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required
虽然文档是精确的,但对于刚开始使用Next.js的人来说,很难理解,有人能尝试提供这些选项的更简单或更具体的概述吗?

wwodge7n

wwodge7n1#

如何测试

首先,在测试以确保我已经理解了它们时,我真的很困惑,因为当你在开发模式下运行时(next dev)的行为与在生产模式下运行时的行为有很大不同(next build && next start),因为它更容易帮助您快速开发。值得注意的是,在开发过程中,每次渲染都会调用getStaticPaths,因此所有内容总是呈现为最新版本,这与产品不同,在产品中可能会启用更多缓存。
这些文档描述了生产行为,因此要进行测试,您确实需要使用生产模式。
下一个问题是我找不到一个可以在示例中创建和更新页面以轻松查看其行为的示例。https://github.com/cirosantilli/node-express-sequelize-nextjs-realworld-example-app同时移植了令人敬畏的Realworld example项目,该项目生成了一个简单的多用户博客网站(迷你中型克隆)。
有了这些工具,我能够确认文档所说的内容。这个答案是在at this commit中测试的,其中包含Next.js10.2.2。

fallback: false

这一条很简单:只有在next build期间生成的页面(即,从getStaticPathspaths属性返回)将是可见的。
例如,如果用户在/post/[post-id]处创建新的博客页面,则之后该页面将不会立即可见,并且访问该URL将导致404。
只有当您重新运行next build,并且getStaticPaths返回paths下的那个页面时,这个新帖子才会变得可见,这是典型用例的情况,其中getStaticPaths返回所有可能的[post-id]

一米十三分一秒

使用此选项时,Next会检查页面是否已预先呈现为.next/server/pages下的HTML。
如果没有:

  1. Next first快速返回一个虚拟预渲染,其中包含在构建时创建的空数据。
    在此情况下,您需要告诉用户页面正在加载。
    您必须行程这种情况,否则可能会因为遗漏属性而掷回例外状况。
    文档中通过检查router.isFallback描述了处理此问题的方法:
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...
    if (router.isFallback) {
        return <div>Loading...</div>
    }

    return <div>post.body</div>

}

因此,在本例中,如果我们没有执行router.isFallback检查,post将是{},而执行post.body将抛出异常
1.在实际页面完成第一次数据呈现(数据在运行时使用getStaticProps获取)后,用户的浏览器会自动更新以查看数据,并将生成的HTML存储在.next/server/pages
然而,如果页面出现在.next/server/pages下,则可能是因为:

  • 它是由next build渲染的
  • 它是在运行时首次呈现的

Next.js只是返回它,而不再次呈现。
因此,如果您编辑帖子,它将不会重新呈现页面缓存。过期的页面将一直被返回,因为它已经存在于.next/server/pages下,所以next不会重新呈现它。
您必须重新执行next build,才能看到网页的更新版本。
因此,这并不是你通常希望的多用户博客。这种方法通常只适用于没有用户生成内容的网站,例如你控制所有内容的电子商务网站。

fallback: true:不存在的页面怎么办?

如果用户访问一个不存在的页面(如/post/i-dont-exist),Next.js将尝试像呈现任何其他页面一样呈现它,因为它检查它是否不在.next/server/pages中,并认为它只是以前没有呈现过。
这与fallback: false不同,在fallback: false中,Next.js在运行时从不生成新页面,而只是返回一个404方向。
在这种情况下,当getStaticProps查询数据库时,您的代码将注意到该页面不存在,然后您告诉Next.js这是一个404,其中包含notFound: true,如以下位置所述:在Next.js中传递动态路由的无效参数时,如何返回404 Not Found页面和HTTP状态?因此Next.js呈现404页面,并且不缓存任何内容。

fallback: 'blocking'

这与fallback: true非常相似,不同之处在于,当第一次命中尚未缓存的页面时,它不会返回虚拟加载页面
相反,它只是让浏览器挂起,直到页面第一次呈现。
但是,将来对该页的请求会很快该高速缓存中得到服务,就像fallback: true一样。
https://dev.to/tomdohnal/blocking-fallback-for-getstaticpaths-new-next-js-10-feature-1727提到了这一点的基本原理,它似乎打破了某些相当具体的功能,通常不是你想要的,除非你需要那些具体的功能之一。
请注意,Next.js文档明确指出,在fallback: true中,它会检测爬虫(具体是如何检测?用户代理还是其他东西?哪些用户代理),并且不会将加载页面返回给爬虫,这将破坏SSR的目的。https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required提到:
注意:这个“回退”版本将不适用于像Google这样的爬虫,而是以阻塞模式呈现路径。
因此,使用'blocking'比使用true在SEO方面似乎没有太大的优势。

但是,如果你的用户是一个安全狂人,并且禁用了JavaScript,他们将只能看到加载页面。你确定Wayback机器不会显示加载页面吗?那么wget呢?因为我喜欢这样的用例,所以我很想在任何地方都使用fallback: 'blocking'

revalidate:增量静态再生(ISR)

当给定revalidate时,对.next/server/pages高速缓存中的页的新请求也会该高速缓存重新生成。这称为“增量静态重新生成”。
revalidate: n表示我们的服务器每n秒最多重新呈现一次。如果在n秒之前有第二个请求进入,则返回先前呈现的页面,并且不触发新的重新呈现。如此大的n意味着用户看到的过期页面更多,但服务器工作负载更少。
因此,大的重新验证可以通过缓存回复来帮助服务器处理大的流量峰值。
如果我们希望网站用户发布和更新他们自己的帖子,我们必须使用以下代码:

  • fallback: truefallback: 'blocking'
  • 以及revalidate: <integer>

revalidatefallback: false没有多大意义。
当给定revalidate: <number>时,行为如下:

  • 如果页面存在于.next/server/pages下,则立即返回此预呈现的页面,可能使用过时的数据呈现。

同时,也用最新的数据启动页面重建。
重建完成后,目标页面不会自动更新为最新版本。用户必须刷新页面才能看到更新后的版本。

  • 否则,如果页面没有缓存,则执行true'blocking'将执行的相同操作,返回虚拟等待页面,或阻塞直到完成,然后创建缓存页面

在通过上述两种情况中的任何一种构建页面后(无论是否是第一次),如果在接下来的number秒内再次访问该页面,则不要触发重建。这样,如果有大量用户访问该网站,大多数请求都不需要昂贵的服务器渲染工作:我们最多每number秒进行一次重新渲染。

显式失效,使用:res.unstable_revalidate

这是目前的测试版,但似乎他们终于引入了一种显式无效页面的方法,如目前在:https://vercel.com/docs/concepts/next.js/incremental-static-regeneration
这样,如果我们能够检测到服务器上的页面过时,我们就能够只在需要的时候重建页面,而不是丑陋的revalidate超时。然后我们每次都可以直接切断。
一旦它变得稳定,它可能会被重命名为res.revalidate
这个令人敬畏的开发是由Sebastian在评论中引起我注意的。

SSR用于单个请求(即忽略revalidate),以便用户可以查看其博客页面编辑的结果

编辑:此用例最好通过即将发布的res.unstable_revalidate/res.revalidate解决。
例如:

  • 博客作者在更新现有帖子后单击提交
  • 他们被重定向到后视图页面,这是通常的行为,看看是否一切正常

他们首先看到的是帖子的过期版本,然后重定向该访问,这将触发一个重建,使用他们在编辑页面中提供的新数据,只有在完成后,用户刷新,他们才会看到更新的页面。
因此,此行为对于编辑器也不是理想的UI行为,因为用户会认为:
刚才发生了什么,我的编辑没有注册吗?
几秒钟。
这可以通过“预览模式”解决,该模式记录在:https://nextjs.org/docs/advanced-features/preview-mode它是在Next.js 12中添加的。预览模式检查是否设置了cookie,如果设置了cookie,则会重新运行getStaticProps,而不考虑revalidate,就像getServerSideProps一样。
然而,即使预览模式也不能很好地解决此用例,因为它不会使缓存无效/更新该高速缓存,这是一个广泛要求的事情,相关:

因此仍然可能发生用户访问没有缓存的页面并看到过时的页面的情况。我可以通过删除cookie并发出额外的GET请求来解决这个问题,但这会产生无用的GET请求并增加更多的复杂性。
我是在以下网站上打开一个关于它的问题后了解到这一点的:https://github.com/vercel/next.js/discussions/25677感谢@sergioengineer指出这一点。
相关主题:

ISR是对SSR的一种优化,然而,像每一种优化一样,它也会增加系统的复杂性。
例如,假设用户可以“收藏”博客帖子。
如果我们使用ISR,则只有预先呈现注销页面才有意义,因为只有预先呈现多个用户共有的内容才有意义。
因此,如果我们要向用户展示这些信息:
我在这一页上加了星了吗?
那么我们必须执行第二个API请求,然后用它更新页面状态。
虽然这听起来很简单,但根据我的经验,这给代码增加了相当大的额外复杂性。

然而,使用SSR,我们可以像往常一样简单地检查用户发送的登录cookie,并在服务器上完全呈现为当前用户定制的页面,这样就不需要进一步的API请求。
因此,只有当你对它进行了基准测试,并且它是值得的时候,你才应该这样做。
以下是检查登录Cookie的示例:https://github.com/cirosantilli/node-express-sequelize-nextjs-realworld-example-app/blob/8dff36e4bcf659fd048e13f246d50c776fff0028/back/IndexPage.ts#L23该示例设置使用了与JavaScript API请求完全相同的SWR令牌,但也通过cookie进行请求。我们不必担心该演示中的XSS,因为我们只在GET请求中使用登录。所有修改请求(如POST)都只通过JavaScript完成,并且不通过cookie进行验证。

ISR梦想:无限revalidate+显式失效+ CDN挂钩

截至Next.js 12,ISR是wonky为这样一个CRUD网站,我真正想要的是事情的工作如下:

  • 当用户创建博客帖子时,我们使用帖子创建挂钩将结果上传到所选的CDN
  • 当用户查看博客帖子时,它直接进入CDN,而不接触服务器。只有当用户想获取用户特定的数据,如“我是否已在此页面上加了星”时,它才向服务器发出一个小的API请求
  • 当用户更新博客帖子时,它只更新所选CDN上的结果

这种方法将真正导致超快的页面加载和最小的服务器工作负载Nirvana。
我想Vercel,Next.js背后的公司,可能会在他们的产品上运行这样的CDN系统,但我不知道如何很好地使用任意选择的CDN,因为我没有看到这样的挂钩。我希望我是错的:-)
但是,即使没有CDN钩子系统,显式失效+无限重新验证已经是一个很好的选择。这可能是随res.unstable_revalidate提供的,请参阅上面的部分。

kgsdhlau

kgsdhlau2#

每当我们想在dynamic routes中实现ISRSSG技术时,我们都应该将我们希望在构建时静态生成的路径传递给getStaticPaths函数。尽管如此,在某些情况下,我们可能会有 * 新的路径 * getStaticPaths未返回的路径,我们必须使用fallback属性处理此路径,该属性也是从getStaticPathsNext.js official docs返回的。

fallback属性可以接受3个值:

1.false:新路径将导致404页面
1.true:将静态生成新路径(调用getStaticProps-****加载状态在生成页面时显示(通过router.isFallback并显示回退页面)
-生成后使用所需属性呈现页面-新路径将缓存在CDN中(以后的请求将生成缓存页面)-Crawler Bot可能会为回退页面编制索引(不利于Seo)
1.
“blocking”:新路径将等待生成HTML(通过SSR-将出现无加载状态
(无回退页面)**-**新路径将缓存在CDN中(以后的请求将生成缓存页面)

注意:在Next.js 12之后,ISR技术中的fallback:true将不会向爬虫Bot显示回退页面阅读更多

pgky5nke

pgky5nke3#

当在我们的应用程序中创建动态页面时(例如视频应用程序),我们需要配置next.js在请求期间如何fallback
如果我们知道页面和我们的应用程序系统是快速的,我们确信我们的数据响应将是即时的,我们可以使用fallback:blocking。我们不需要显示加载状态,因为Next.js将等待这个页面在服务器上完全预生成,然后才提供服务。
fallback:false中,如果没有找到新页面,则会显示404个页面。如果您希望在构建时生成所有动态路径,则会使用false。在这种情况下,在getStaticPath中,您需要获取数据库中有多少项。由于预构建的页面是从CDN提供的,因此其查找时间非常快,您实际上并不是在获取数据,所以你不需要“加载状态”。你只是检查给定的URL路径是否有一个预先生成的页面。如果将来你需要添加更多的路径,你需要重建你的应用程序。
在你的视频应用中,你可能有太多的视频,所以你只预建了最流行的视频页面。如果用户访问了一个视频,而它的页面不是预生成的,你必须进行数据提取,所以你需要一个“加载”状态。现在你需要设置fallback:true。由于数据提取需要时间,如果你在加载时不显示不同的组件,你可能会得到像“不能读取属性“标题”的未定义”,因为此时视频的标题还没有定义。

function Video({ videoId }) {
  const router = useRouter()   
  // If the page is getting generated  
  if (router.isFallback) {
    return <div>Loading...</div>
  }    
  // then  return the main component
}

相关问题