作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Subhakar Tikkireddy
验证专家 在工程
7 的经验

Subhakar是一名前端开发人员,拥有使用反应, 下一个的丰富经验.js和TypeScript. 他专门为初创公司创建mvp,并参与了从跨平台渐进式web和反应 Native应用程序到ai支持的浏览器扩展和超过10个使用的加密web应用程序的项目,000年投资者.

以前的角色

开发人员反应
分享

下一个.Js提供的远不止标准的服务器端呈现功能. 软件工程师可以通过多种方式配置他们的web应用程序来优化下一个.js性能. 事实上, 下一个.js开发人员 通常使用不同的缓存策略, 各种预渲染技术, 并对动态组件进行优化和定制 下一个.js呈现 满足特定要求.

当你的目标是开发一个包含数万个页面的多页面可扩展web应用程序时, 更重要的是要保持良好的平衡.Js页面加载速度和最佳服务器负载. 选择正确的渲染技术对于构建一个不会浪费硬件资源和产生额外成本的高性能web应用程序至关重要.

下一个.预渲染技术

下一个.Js默认预渲染每个页面, 但性能和效率可以进一步提高使用不同的下一个.js 呈现类型 和方法 预渲染和渲染. 除了传统的 客户端渲染 (CSR),下一个.Js为开发者提供了两种基本的预渲染形式:

  • 服务器端呈现 (SSR) 处理在运行时调用请求时呈现网页. 这种技术增加了服务器负载,但是如果页面有动态内容并且需要社会可见性,这种技术是必不可少的.

  • 静态站点生成 (江源发展促进会) 主要处理在构建时渲染网页. 下一个.Js为带有或不带有数据的静态生成提供了额外的选项, 以及自动静态优化, 哪个决定页面是否可以预呈现.

预渲染对于需要社会关注(开放图谱协议)和良好的SEO(元标签)但包含基于路由端点的动态内容的页面很有用. 例如,X(以前的Twitter)用户页面包含 / @推特_name 端点具有特定于页面的元数据. 因此,在此路径中预呈现所有页面是一个不错的选择.

元数据并不是选择SSR而不是csr的唯一原因——在服务器上呈现HTML也可以显著改进 第一次输入延迟 (FID), 核心Web生命指标,衡量从用户第一次交互到浏览器实际能够处理响应的时间. 在客户端呈现繁重(数据密集型)组件时, FID对用户来说变得更加明显, 尤其是那些网速较慢的人.

如果下一个.Js的性能优化是重中之重, 不能在服务器端过度填充DOM树, 这会使HTML文档膨胀吗. 如果内容属于页面底部的列表,并且在第一次加载时不能立即显示, 对于该特定组件,客户端呈现是更好的选择.

具有页面类型的预呈现样例表, 元数据, 以及举例的内容, 包括亚马逊, 脸谱网, Toptal, 和YouTube.

通过确定以下因素,可将预绘制进一步划分为多个优化方法 可变性、批量大小以及更新和请求的频率. We must pick the appropriate strategies while keeping in mind the server load; we don’t want to adversely affect the user experience or incur unnecessary hosting costs.

确定下一步的因素.js性能优化

就像传统的服务器端呈现在运行时对服务器施加高负载一样, 纯静态生成将在构建时产生高负载. 我们必须根据网页和路由的性质来谨慎地配置渲染技术.

在处理下一个时.js优化, 提供的选项非常多,我们必须为每个路由端点确定以下标准:

  • 变化: 网页的内容也一样 与时间有关的 (每分钟变化), 行动的依赖 (当用户创建/更新文档时更改),或者 不新鲜的 (在新版本之前不会更改).
  • 批量大小: 该路由端点中最大页面数的估计(e.g.例如,一个流媒体应用中有30种类型)。.
  • 更新频率: 估计的内容更新率(e.g.(每月10次更新),无论是时间依赖还是行动依赖.
  • 请求频率: 用户/客户端对网页的估计请求率(例如.g.(每天100个请求,每秒10个请求).

低体积大小和随时间变化的可变性

增量静态再生(ISR)以指定的时间间隔重新验证网页. 这是网站中标准构建页面的最佳选择, 数据应该在什么地方每隔一段时间刷新一次. 例如,有一个 类型/ genre_id 像Netflix这样的顶级媒体应用的路由点, 每个类型页面都需要每天更新新鲜内容. 因为游戏类型的整体规模很小(约200个)。, 选择ISR是更好的选择, 在预构建/缓存的页面超过一天的情况下,重新验证页面.

下面是一个ISR实现的例子:

getStaticProps() {
  Const posts = await fetch(url-endpoint).then((data)=>data.json ());
  
  /*最多每10秒重新验证一次*/
  返回{props: {posts},重新验证:10,}
}

getStaticPaths() {

  Const posts = await fetch(url-endpoint).then((data)=>data.json ());
  Const paths = posts.map((post) => (
参数:{id: post.id },
  }));

  返回{paths, fallback: false}
}

在本例中为下一个.Js最多每10秒重新验证一次所有这些页面. 这里的关键是 最多,因为页面不是每10秒重新生成一次,而是只在请求进入时重新生成一次. 下面是它是如何工作的一步一步的演练:

  • 用户请求ISR页面路由.
  • 下一个.Js发送缓存的(过期的)页面.
  • 下一个.Js尝试检查过期页面是否老化超过10秒.
  • 如果是,下一步.Js生成新页面.

一个ISR示例表, 按页面类型组织, 批量大小, 和可变性, 比如YouTube, IMDB, 和eBay.

高体积大小和随时间变化

大多数服务器端应用程序都属于这一类. 我们称它们为公共页面,因为这些路由可以缓存一段时间,因为它们的内容不依赖于用户, 数据不需要一直是最新的. 在这些情况下, 散装尺寸通常过高(~ 200万), 在构建时生成数百万个页面并不是一个可行的解决方案.

SSR和缓存:

更好的选择总是做服务器端渲染,例如.e.,以便在运行时生成网页 当请求 在服务器上 缓存页面 整整一天、一小时或一分钟,以便以后的任何请求都将获得缓存页面. 这确保了应用程序不需要在构建时构建数百万个页面, 不要在运行时重复构建相同的页面.

让我们来看一个SSR和缓存实现的基本示例:

导出异步函数getServerSideProps({req, res}) {
  /*设置缓存为10秒*/
  res.setHeader('cache - control','公共, s-maxage=10')
  Const data = fetch(url-endpoint).then((res) => res.json ());
  返回{
    道具:{data};
  }
}

你可以检查 下一个.Js缓存文档 如果您想了解更多关于缓存头的信息.

ISR和回退:

尽管在构建时生成数百万个页面并不是一个理想的解决方案, 有时我们确实需要在构建文件夹中生成它们,以便进一步配置或 定制的回滚. 在这种情况下, 我们可以选择在构建步骤中绕过页面生成, 仅对生成的网页的第一个请求或任何后续请求(重新验证间隔)按需渲染.

我们从添加 {后退:“阻塞”}getStaticPaths, 当构建开始时, 我们关闭API(或阻止访问它),这样它就不会生成任何路径路由. 这有效地绕过了在构建时不必要地构建数百万个页面的阶段, 而是在运行时按需生成它们,并将结果保存在构建文件夹中(_next /静态)用于后续的请求和构建.

下面是一个在构建阶段限制静态生成的例子:

getStaticPaths() {
  // fallback: 'blocking'将尝试服务器渲染 
  //如果页面不存在,则按需调用所有页面.
  如果(过程.env.SKIP_BUILD_STATIC_GENERATION) {
	返回{paths: [], fallback: 'blocking'};
  }
}

现在,我们希望生成的页面在一段时间内进入缓存,并在稍后经过缓存周期时重新验证. 我们可以使用与ISR示例相同的方法:

getStaticProps() {
  const posts = await fetch().then((data)=>data.json ());

  //每10秒重新验证一次.
  返回{props: {posts},重新验证:10,}
}

如果10秒后有新的请求, 该页将被重新验证(如果该页尚未构建,则无效)。, 有效地以与SSR和缓存相同的方式工作, 但是在生成输出文件夹(/ _next /静态).

按页面类型划分的SSR加缓存和ISR加回退示例, 批量大小, 和可变性, 包括IMDB, YouTube, 和Toptal.

在大多数情况下,带缓存的SSR是更好的选择. ISR和回退的缺点是页面最初可能显示过时的数据. 在用户访问页面之前,页面不会重新生成(以触发重新验证), 然后,同一用户(或另一个用户)访问同一页面以查看该页面的最新版本. 这确实不可避免地会导致用户A看到过时的数据,而用户B看到准确的数据. 对于一些应用来说,这是微不足道的,但对于其他应用来说,这是不可接受的.

Content-dependent可变性

按需重新验证(ODR)通过webhook在运行时重新验证网页. 这对下一个非常有用.Js的速度优化,在情况下,页面需要更真实的内容,如.g., 如果我们正在用无头CMS构建一个博客,当内容创建或更新时提供webhook. 我们可以调用相应的API端点来重新验证网页. 后端REST api也是如此——当我们更新或创建文档时, 我们可以调用请求来重新验证网页.

让我们来看一个实际的ODR示例:

//调用此URL将重新验证文章.
// http:///api/重新验证?重新验证_path=&秘密=

/ /页面/ api /重新验证.js

导出默认异步函数h和ler(req, res) {
  如果要求的事情.查询.秘密 != =过程.env.MY_SECRET_TOKEN) {
    返回res.状态(401).json({message: '无效令牌'})
  }

  尝试{
    等待res.重新验证('http:///'+req.查询.重新验证_path)
    返回res.Json({重新验证:true})
  } catch (err) {
    返回res.状态(500).发送(错误重新验证)
  }
}

如果我们有很大的体积(约200万), 我们可能想通过传递一个空的路径数组来跳过构建阶段的页面生成:

getStaticPaths() {
  Const posts = await fetch(url-endpoints).then((res) => res.json ());

  //如果路径不存在,将根据需要尝试服务器渲染所有页面.
  返回{paths: [], fallback: 'blocking'};
}

这防止了ISR中描述的缺点. 而不是, 用户A和用户B都将在重新验证时看到准确的数据, 生成的再生在后台进行,而不是在请求时进行.

根据页面类型的按需静态重新验证案例示例, 可变性, 更新频率, 请求频率, 包括Toptal, Vimeo, 和YouTube.

在某些情况下,依赖于内容的可变性可以被强制转换为依赖于时间的可变性, i.e.,如果批量大小和更新或请求频率太高.

让我们以IMDB电影详细信息页面为例. 虽然可能会添加新的评论或分数可能会改变,re is no need to reflect the details within seconds; even if it is an hour late, 它不会影响应用程序的功能. 然而, 通过切换到ISR,可以极大地减少服务器负载, 因为您不希望每次用户添加评论时都更新电影详细信息页面. 从技术上讲, 只要更新频率高于请求频率, 可以强制切换.

随着 反应 反应 18中的服务器组件, 布局RFC 是下一个最期待的功能更新之一.Js平台将支持单页应用程序, 嵌套布局, 还有一个新的路由系统. 布局RFC支持改进的数据获取,包括并行获取,这允许下一个.Js在数据获取完成之前开始渲染. 使用顺序数据获取, 依赖于内容的呈现只有在之前的获取完成之后才有可能.

下一个.js的混合CSR方法

在未来.在Js中,客户端渲染总是在预渲染之后进行. 它通常被视为一种附加呈现类型,在我们需要减少服务器负载的情况下非常有用, 或者如果页面有组件可以 懒加载. 预渲染和CSR的混合方法在许多情况下都是有利的.

如果内容是动态的,并且不需要 开放图谱 集成时,我们应该选择客户端呈现. 例如, 我们可以选择SSG/SSR在构建时预呈现一个空布局,并在组件加载后填充DOM.

在这种情况下,元数据通常不受影响. 例如,脸谱网的主页feed每60秒更新一次.e.,可变内容). 但是,页面元数据仍然保持不变.g.(页面标题、主页提要),因此不会影响开放图谱协议和 SEO的可见性.

具有页面类型的客户端呈现示例, 元数据, 内容, 包括脸谱网, YouTube, 亚马逊, 和Netflix.

动态组件

客户端呈现适用于第一次加载时在窗口框架中不可见的内容, 或默认隐藏的组件,直到动作(例如.g.、登录模式、警报、对话). 您可以通过在呈现之后加载内容(如果要呈现的组件已经在jsbundle中)或延迟加载组件本身来显示这些组件 通过 下一个/动态.

通常, 网站渲染从纯HTML开始, 然后是页面的聚合和客户端呈现技术,例如在组件加载或动态组件上获取内容.

水合作用 是一个过程,其中反应使用JSON数据和JavaScript指令使组件交互(例如, 将事件处理程序附加到按钮). 这通常会让用户觉得页面加载速度有点慢, 就像在一个空的X配置文件布局中,配置文件内容是逐步加载的. 有时最好通过预渲染来消除这种情况, 特别是如果内容在预渲染时已经可用.

悬念阶段 表示动态组件加载和呈现的间隔时间. 在未来.在Js中,我们可以选择在此阶段呈现占位符或回退组件.

左边是水合作用后的一页, 接下来是悬念阶段和动态组件的下载, 在右边的渲染组件中结束.
渲染动态组件

在下一个中导入动态组件的示例.js:

/*加载客户端组件*/
const Dynamic模态 = dynamic(() => import('../组件/模态”),{
  ssr:假的,
})

你可以在动态组件加载时渲染回退组件:

/*阻止水合作用直到悬疑*/
const Dynamic模态 = dynamic(() => import('../组件/模态”),{
  悬念:没错,
})
导出默认函数Home() {
  回报(
    <悬念 fallback={`Loading...`}>
      
    
  )

请注意, 下一个/动态 有一个 悬念 回调以显示加载器或空布局,直到组件加载, 因此,头组件不会包含在页面的初始JavaScript包中(减少初始加载时间)。. 页面将呈现 悬念 首先是回退组件,然后是 模态 组件时, 悬念 边界已解决.

下一个.js缓存:技巧和技术

如果您需要在提高页面性能的同时减少服务器负载, 缓存 你的武器库中最有用的工具是什么. 在SSR和缓存中, 我们已经讨论了缓存如何有效地提高具有大批量大小的路由点的可用性和性能. 通常,所有下一个.Js的资产(页面), 脚本, 图片, 视频)有缓存配置,我们可以添加和调整以适应我们的需求. 在对此进行研究之前,让我们简要介绍一下缓存的核心概念. 当用户在网络浏览器中打开任何网站时,网页的缓存必须通过三个不同的检查点:

  • 浏览器缓存是所有HTTP请求的第一个检查点. 如果有缓存命中,它将直接从浏览器缓存存储中提供, 而缓存丢失将传递到下一个检查点.
  • 内容分发网络(CDN)缓存是第二个检查点. 它是一个缓存存储,分布在全球不同的代理服务器上. 这也被称为边缘缓存.
  • 源服务器是第三个检查点, 如果缓存存储推送一个重新验证请求(i.e.,缓存中的页面已经过期).

缓存头被添加到所有来自的不可变资产中 / _next /静态,如CSS、JavaScript、图片等;

cache - control: 公共, maxage=31536000,不可变

下一个的缓存头.. Js的服务器端呈现由 cache - controlgetServerSideProps:

res.setHeader('cache - control', '公共', 's-maxage=10', '不新鲜的-while-重新验证=59');

但是,对于静态生成的页面(ssg),缓存头是由 重新验证 选项 getStaticProps.

理解和配置缓存头

只要您了解如何正确配置,编写缓存头就很简单. 让我们检查一下每个标签的含义.

公众对. 私人

要做的一个重要决定是在两者之间做出选择 私人公共. 公共 表示响应可以存储在共享缓存(CDN、代理缓存等)中.),而 私人 指示响应只能存储在私有缓存(浏览器中的本地缓存)中。.

如果该页面针对许多用户,并且对这些用户来说看起来是相同的,那么选择 公共,但如果它是针对个人用户的,那就选择 私人.

私人 很少在web上使用,因为大多数时候开发人员都试图利用边缘网络来缓存他们的页面, 而 私人 将主要防止这种情况并在用户端本地缓存页面. 私人 如果页面是特定于用户的并且包含私人信息,则应该使用I.e.,我们不希望在公共缓存存储中缓存的数据:

cache - control: 私人, s-maxage=1800

最高年龄

s-maxage 缓存页面的最大年龄是多少.e., 多长时间才算新鲜?, 如果请求超过指定的值,则会进行重新验证. 虽然也有例外, s-maxage 应该适合大多数网站吗. 你可以根据你的分析和内容变化的频率来决定它的价值. 如果同一个页面每天有一千次点击,而内容每天只更新一次, 然后选择一个 s-maxage 24小时的值.

必须重新验证vs. 重新验证时失效

must-重新验证 指定缓存存储中的响应可以被重用,只要它是新鲜的, 但如果过期就必须重新验证. 不新鲜的-while-重新验证 指定缓存存储中的响应可以被重用,即使它在指定的时间段内已经过期(因为它在后台重新验证)。.

如果您知道内容将在给定的时间间隔内发生更改,则使用预先存在的内容 must-重新验证. 例如, 您可以将其用于股票交易所站点,其中价格每天都在波动,旧数据很快就会失效.

相比之下, 不新鲜的-while-重新验证 当我们知道内容每隔一段时间都在变化时使用, 过去的内容被弃用, 但也不是完全无效. 想象一下流媒体服务的十大趋势页面. 内容每天都在变化, 但将前几次点击显示为旧数据是可以接受的, as the first hit will 重新验证 the page; technically speaking, 如果网站流量不是太高,这是可以接受的, 或者内容不重要. 如果流量非常大的话, 然后,在重新验证页面所需的不到一分钟的时间内,可能会有一千名用户看到错误的页面. 经验法则是确保内容更改不是高优先级.

根据重要程度,您可以选择在一段时间内启用过期页面. 这个周期通常是59秒,因为大多数页面需要一分钟来重建:

cache - control: 公共, s-maxage=3600, 不新鲜的-while-重新验证=59

陈旧If错误

另一个有用的配置是 不新鲜的-if-error:

cache - control: 公共, s-maxage=3600, 不新鲜的-while-重新验证=59, 不新鲜的-if-error=300

假设页面重建失败, 并且由于服务器错误而不断失败, 这限制了陈旧数据的使用时间.

下一个的未来.js呈现

没有适合所有需求和目的的完美配置, 而最好的方法往往取决于类型 web应用程序. 但是,您可以从确定因素并选择正确的下一个开始.Js的渲染类型和技术为您的需要.

需要特别注意缓存设置,这取决于每天预期的用户数量或页面浏览量. 具有动态内容的大型应用程序需要更短的缓存周期,以获得更好的性能和可靠性, 但小规模应用的情况正好相反.

虽然本文中演示的技术应该足以涵盖几乎所有场景, Vercel经常发布下一个.Js更新并添加新特性. 与渲染和性能相关的最新添加保持同步.g., 应用路由器功能 在未来.Js 13)也是性能优化的重要组成部分.

Toptal工程博客的编辑团队向 穆出来 查看本文中提供的代码示例和其他技术内容.

了解基本知识

  • 下一步是什么?.用于?

    下一个.js是一个流行的基于react的框架,用于生产级、可扩展的web应用程序. 它提供了一些独特的特性,比如无服务器api、各种预渲染技术和代码分割.

  • 什么时候应该使用下一个.js?

    下一个.当你正在开发的应用程序将受益于预渲染功能时,应该使用Js. 如果性能、可伸缩性和适应性是你对web应用程序的主要要求,下一个.j应该在你的清单的顶部.

  • 是下一个.Js很适合客户端渲染?

    是的,尽管人们普遍误解下一个.Js是仅用于服务器端呈现的好选择. 下一个.Js客户端呈现的性能与类似框架相当, 考虑到下一个中不同呈现技术提供的可伸缩性优势.js.

  • 如何在下一个中提高页面速度.js?

    下一个.Js预呈现技术可以帮助实现最佳性能,具体取决于页面类型. 在大多数情况下, 使用启用缓存的服务器端呈现或静态站点生成是增加下一个的最佳方法.Js页面速度.

聘请Toptal这方面的专家.
现在雇佣
Subhakar Tikkireddy

Subhakar Tikkireddy

验证专家 在工程
7 的经验

维杰亚瓦达,安得拉邦,印度

自2022年9月8日起成为会员

作者简介

Subhakar是一名前端开发人员,拥有使用反应, 下一个的丰富经验.js和TypeScript. 他专门为初创公司创建mvp,并参与了从跨平台渐进式web和反应 Native应用程序到ai支持的浏览器扩展和超过10个使用的加密web应用程序的项目,000年投资者.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前的角色

开发人员反应

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

Toptal开发者

加入总冠军® 社区.