使用 Netlify 和 Next.js 分解庞大的构建
优采云 发布时间: 2022-05-08 04:09使用 Netlify 和 Next.js 分解庞大的构建
静态生成非常适合性能——直到应用程序变得太大并且构建时间过长。今天,我们将看看 Netlify 的新按需构建器如何解决这个问题。此外,我们将它与 Next.js 的增量静态再生相结合,以获得最佳的用户和开发人员体验。当然,还要对这些结果进行基准测试!
使用静态生成的网站的最大痛苦之一是随着应用程序的增长,构建速度会越来越慢。这是任何堆栈在某些时候都面临的不可避免的问题,并且根据您使用的产品类型,它可能会从不同的角度发生。
例如,如果您的应用在生成部署工件时有多个页面(视图、路由),则这些路由中的每一个都将成为一个文件。然后,一旦达到数千人,您就会开始想知道何时可以部署而无需提前计划。这种情况在电子商务平台或博客上很常见,它们已经是网络的很大一部分,但不是全部。不过,路线并不是唯一可能的瓶颈。
一个资源密集型的应用程序也最终会到达这个转折点。许多静态*敏*感*词*进行资产优化以确保最佳的用户体验。如果没有构建优化(增量构建、缓存,我们将很快实现),这最终也将变得难以管理——考虑遍历网站中的所有图像:一遍又一遍地调整大小、删除和/或创建新文件。一旦所有这些都完成了:记住 Jamstack 从内容交付网络的边缘为我们的应用程序提供服务。所以我们仍然需要将东西从编译它们的服务器移动到网络的边缘。
除此之外,还有另一个事实:数据通常是动态的,这意味着当我们构建和部署应用程序时,可能需要几秒钟、几分钟甚至一个小时。与此同时,世界一直在旋转,如果我们从其他地方获取数据,我们的应用程序肯定会过时。不能接受!再次构建以更新!
构建一次,需要时更新
一段时间以来,基本上每个 Jamstack 平台、框架或服务都将解决庞大的构建问题放在首位。许多解决方案都围绕增量构建。实际上,这意味着构建将与它们与当前部署的差异一样庞大。
不过,定义差异算法并非易事。为了让最终用户真正受益于这种改进,必须考虑缓存失效策略。长话短说:我们不想让没有改变的页面或资产的缓存失效。
Next.js 提出了增量静态再生 ( ISR )。本质上,它是一种为每条路由声明我们希望它重建的频率的方法。在底层,它简化了服务器端的大量工作。因为每条路由(无论是否动态)都会在特定的时间范围内重建自身,并且它完全符合 Jamstack 公理,即每次构建时都使缓存无效。将其视为max-age标题,但用于 Next.js 应用程序中的路由。
要启动您的应用程序,ISR 只需一个配置属性即可。在您的路由组件(/pages目录内)上,转到您的getStaticProps方法并将revalidate密钥添加到返回对象:
export async function getStaticProps() { const { limit, count, pokemons } = await fetchPokemonList()<br /> return { props: { limit, count, pokemons, }, revalidate: 3600 // seconds }}
上面的代码片段将确保我的页面每小时重建一次并获取更多的神奇宝贝来显示。
我们仍然时不时地获得批量构建(在发布新部署时)。但这允许我们将内容与代码分离,通过将内容移动到内容管理系统(CMS),我们可以在几秒钟内更新信息,无论我们的应用程序有多大。告别用于更新拼写错误的 webhook!
按需构建器
Netlify 最近推出了On-Demand Builders,这是他们支持 Next.js 的 ISR 的方法,但也适用于包括 Eleventy 和 Nuxt 在内的框架。在上一届会议中,我们确定 ISR 是朝着缩短构建时间迈出的一大步,并解决了很大一部分用例。尽管如此,还是有一些警告:
Netlify 的新部署基础架构允许开发人员创建逻辑来确定他们的应用程序的哪些部分将基于部署构建以及哪些部分将被推迟(以及它们将如何被推迟)。
创建按需构建器
首先,将netlify/functions包作为 a添加devDependency到您的项目中:
yarn add -D @netlify/functions
一旦完成,它就像创建一个新的Netlify Function一样。如果您没有为它们设置特定目录,netlify/functions/请继续为您的构建器创建一个任意名称的文件。
import type { Handler } from '@netlify/functions'import { builder } from '@netlify/functions'<br />const myHandler: Handler = async (event, context) => { return { statusCode: 200, body: JSON.stringify({ message: 'Built on-demand! ' }), }}export const handler = builder(myHandler)
正如您从上面的代码片段中看到的,按需构建器与常规的 Netlify 函数分开,因为它将其处理程序包装在一个builder()方法中。此方法将我们的功能连接到构建任务。这就是您只需要在必要时才延迟构建应用程序所需的全部内容。从一开始就进行小型增量构建!
Netlify 上的 Next.Js
要在 Netlify 上构建 Next.js 应用程序,应该添加 2 个重要的插件来获得更好的体验:Netlify Plugin Cache Next.js和Essential Next-on-Netlify。前者更有效地缓存您的 NextJS,您需要自己添加它,而后者对 Next.js 架构的构建方式进行了一些细微调整,因此它更适合 Netlify,并且默认情况下可用于 Netlify 可以识别的每个新项目是使用 Next.js。
使用 Next.Js 的按需构建器
构建性能、部署性能、缓存、开发人员体验。这些都是非常重要的主题,但数量很多——并且需要时间来正确设置。然后我们开始讨论关于专注于开发人员体验而不是用户体验的旧讨论。这是事情进入积压中的隐藏位置以被遗忘的时间。并不真地。
Netlify 得到了您的支持。只需几个步骤,我们就可以在 Next.js 应用程序中利用 Jamstack 的全部功能。现在是时候卷起袖子,把它们放在一起了。
定义预渲染路径
如果您之前在 Next.js 中使用过静态生成,您可能听说过getStaticPaths方法。此方法适用于动态路由(将呈现各种页面的页面模板)。无需过多关注此方法的复杂性,重要的是要注意返回类型是具有 2 个键的对象,就像在我们的概念验证中,这将是[Pokémon] 动态路由文件:
export async function getStaticPaths() { return { paths: [], fallback: 'blocking', }}
在我们的案例中,我们getStaticPaths正在确定:
不会预渲染任何路径;
每当调用此路由时,我们都不会提供备用模板,我们将按需呈现页面并让用户等待,从而阻止应用程序执行任何其他操作。
使用 On-Demand Builders 时,请确保您的后备策略符合您应用程序的目标,Next.js 官方文档:后备文档非常有用。
在 On-Demand Builders 之前,我们getStaticPaths略有不同:
export async function getStaticPaths() { const { pokemons } = await fetchPkmList() return { paths: pokemons.map(({ name }) => ({ params: { pokemon: name } })), fallback: false, }}<br />
我们正在收集我们打算拥有的所有神奇宝贝页面的列表,将所有pokemon对象映射到仅带有神奇宝贝名称的 a ,并将携带它string的对象转发返回到。我们的设置是因为如果路由不匹配,我们希望 Next.js 抛出一个页面。{ params }getStaticPropsfallbackfalse404: Not Found
您可以检查部署到 Netlify 的两个版本:
该代码也在Github 上开源,您可以轻松地自行部署以检查构建时间。有了这个队列,我们进入下一个主题。
构建时间
如上所述,前面的演示实际上是一个概念验证,如果我们无法衡量,没有什么是真正的好或坏。对于我们的小研究,我去了PokéAPI并决定捕捉所有的神奇宝贝。
出于可重复性的目的,我限制了我们的请求 (to 1000)。这些实际上并不全部在 API 中,但它强制所有构建的页面数量相同,无论内容是否在任何时间点得到更新。
export const fetchPkmList = async () => { const resp = await fetch(`${API}pokemon?limit=${LIMIT}`) const { count, results, }: { count: number results: { name: string url: string }[] } = await resp.json() return { count, pokemons: results, limit: LIMIT, }}
然后在单独的分支中将两个版本发射到 Netlify,这要归功于预览部署,它们可以在基本相同的环境中共存。为了真正评估两种方法之间的差异,ODB 方法非常极端,没有为该动态路由预渲染任何页面。虽然不推荐用于现实世界的场景(您将希望预渲染您的交通繁忙的路线),但它清楚地标志着我们可以通过这种方法实现的构建时性能改进的范围。
战略页数资产数量构建时间总部署时间
完全静态生成
1002
1005
2分32秒
4分15秒
按需构建器
2
0
52 秒
52 秒
我们的 PokéDex 小应用程序中的页面非常小,图像资源非常精简,但部署时间的收益非常显着。如果一个应用有中到大量的路由,绝对值得考虑ODB策略。
它使您的部署更快,因此更可靠。性能下降仅发生在第一个请求上,从后续请求开始,渲染的页面将被缓存在边缘,使得性能与完全静态生成的性能完全相同。
未来:分布式持久渲染
就在同一天,按需构建器被宣布并提前访问,Netlify 还发布了他们的关于分布式持久渲染 (DPR) 的评论请求。
DPR 是 On-Demand Builders 的下一步。它利用这种异步构建步骤,然后缓存资产直到它们实际更新,从而利用更快的构建。不再为 10k 页面的网站构建完整版本。DPR 使开发人员能够通过可靠的缓存和使用按需构建器来完全控制构建和部署系统。
想象一下这个场景:一个电子商务网站有 10k 个产品页面,这意味着构建整个应用程序进行部署大约需要 2 个小时。我们不需要争论这是多么痛苦。
使用 DPR,我们可以在每次部署时设置前 500 个页面。我们最繁忙的流量页面始终为我们的用户准备好。但是,我们是一家商店,即每一秒都很重要。所以对于其他 9500 个页面,我们可以设置一个构建后挂钩来触发它们的构建器——异步部署我们剩余的页面并立即缓存。没有用户受到伤害,我们的网站以最快的速度更新,缓存中不存在的所有其他内容都被存储了。
结论
尽管本文中的许多讨论点都是概念性的,并且有待定义实现,但我对 Jamstack 的未来感到兴奋。作为一个社区,我们正在取得的进步围绕最终用户体验展开。
您对分布式持久渲染有何看法?您是否在应用程序中尝试过 On-Demand Builders?在评论中让我知道更多信息或在 Twitter 上给我打电话。我真的很好奇!
如果看了觉得有帮助的,请关注兴码通,兴子会持续分享更多知识点,前端后端都会有哦!
-END-
点击关注兴码通,回复“1024”获取2TB学习资源!
点击关注兴码通,回复“前端干货合集”获取更多前端干货知识!