前言

术语说明:

  • SSR —— 服务端渲染
  • SSG —— 静态生成
  • ISR —— 增量静态化
  • Date Fetch 函数 —— 本文特指服务端数据获取的几种函数 getStaticPropsgetServerSidePropsgetInitialPropsgetStaticPaths

Next.js 中最突出的莫过于它的渲染模式,之前写过一篇文章 《Next.js之前端渲染模式》 中分别介绍和对比了几种渲染模式的优劣势,而且其中的 ISR 在大部分的业务页面中能起到很关键性的性能优化作用。

最近在使用 ISR 功能的时候遇到了一些问题,本篇文章将分享如何更好的去使用 ISR,以及在使用的过程中可能会遇到的一些问题和解决方法,其中会涉及到一些原理的探索。

为何要使用 ISR

先分享一下为何要使用 ISR ,可能有人还不是很理解这个 ISR 是什么。

Next.JS 项目打包时,使用 getStaticProps 或者不使用 Date Fetch 函数的页面会默认 静态化 ,也就是会生成[pageName].html 的 html 文件,用户访问就后 Next 服务直接读取此 html 文件,不再去动态渲染内容,也就减少了接口的请求,因此,相比 SSR 渲染模式,能极大的减少页面访问时间(大部分页面在正常网速能控制在 1s 内显示)和降低服务器的压力。

使用 getStaticProps 的静态化页面会导致了一个问题,接口内容更新后,用户访问页面获取到的信息并不会更新,因此需要一种可以在服务运行中动态去触发 SSG 生成的 html 的能力,于是就出现了 ISR,让 SSG 也能拥有增量更新的能力。

使用和功能验证

增量静态化一般有两种使用方式:定时更新指令更新

写了一个 demo 工程,可以去自己 clone 下来尝试,下面会也会详细介绍demo实现的主要过程和用里面的 3 个 demo 示例的运行结果来检验一些理论。

ISR —— 定时更新

新建页面:

// src/pages/isr/demo1/index.js
const Demo1 = ({ times }) => {return (<div><h2>定时刷新</h2><div>刷新次数:{times}</div></div>)
}let times = 0;export async function getStaticProps() {times += 1console.log(times)return {props: {times,},revalidate: 10, // 10秒后访问触发更新}
}
复制代码

开发环境 静态化 表现和 ssr 一样,因此,需要需要打包后运行才能看出效果,运行 build 命令:

# 构建打包
pnpm build
复制代码

构建过程中会打印 1 这时候可以看一下构建产物:

构建产物 .next/server/pages/isr 目录下已经生成了一个 demo1 的 html 文件,注意这时候 times1

pnpm start 启动工程后访问demo1页面:

vscode 的控制台打印输出为1(build 和 start 都会重新初始化 times),这时候会触发构建;

构建完成之后,再次访问会发现跟首次访问一样的,上面显示的刷新次数还是 1 ,但是 vscode 的控制台印输出为2 ,这是因为访问的时候是去构建新的页面,仍然返回上一次构建成功的页面内容,并不会返回当前正在构建的内容。以后每次访问时,控制台打印的数字都会比正在显示的大。

上面的这种现象也能说明官方文档上阐述的一个结论:

When a request is made to a page that was pre-rendered at build time, it will initially show the cached page 当发出请求让页面进行构建时,它会先返回缓存页面。

ISR —— 指令更新

我们新建一个 demo2 页面,复制 demo1 即可,然后去除 getStaticProps 函数返回的 revalidate 字段:

// src/pages/isr/demo2/index.js
export async function getStaticProps() {times += 1console.log(times)return {props: {times,}}
}
复制代码

新增一个触发页面更新的接口指令:

// src/pages/api/revalidate.js
export default async function revalidateHandler(req, res) {// 指令密钥校验if (req.query.secret !== MY_SECRET_TOKEN) {return res.status(401).json({ message: 'Invalid token' })}try {// 更新 demo2await res.revalidate('/isr/demo2')// 返回说明更新指令已发出,并不能说明一定更新成功return res.json({ revalidated: true })} catch (err) {// 更新失败return res.status(500).send('Error revalidating')}
}
复制代码

运行 pnpm build && pnpm start ,访问 demo2 页面 不管访问多少次,getStaticProps 都不会触发,页面上都显示刷新次数为 1

然后访问 http://localhost:3000/api/revalidate?secret=MY_SECRET_TOKEN,访问后,电脑控制台会打印2,但如果立马快速去访问页面,页面上显示刷新次数还会是 1 ,因为构建还是需要一定的时间,稍微等一下再去访问就成了 2,一般时间在一两秒以上。

demo 工程我部署到了 vercel,可以点击 页面 和 指令 直接去验证,不过因为是外网,有可能访问不了。

ISR —— 动态路由

上面的两个案例只是一个单页面,如果是动态路由页面,那么也可以添加 getStaticPaths 来实现。

demo3 在 demo2 的基础上,修改了一下文件名命名方式,demo3/index.js 改成 demo3/[id].js,内容新增 getStaticPaths 内容:

export async function getStaticPaths() {// 从接口获取文章列表,使用的时候不用本站的接口,这样在打包的时候会出错。const res = await fetch('http://localhost:3000/api/posts/list')const posts = await res.json()// 处理成 getStaticPaths 需要的返回参数const paths = posts.map((post: any) => ({params: { id: post.id },}))return { paths, fallback: 'blocking' }
}
复制代码

对应的修改一下页面内容:

const Demo3 = ({ name, content }) => {return (<div><h2>{name}</h2><div dangerouslySetInnerHTML={{ __html: content }}></div></div>)
}export async function getStaticProps(ctx) {// 获取详情const res = await fetch(`http://localhost:3000/api/posts/detail?id=${ctx.params?.id}`);const detail = await res.json()return {props: {...detail}}
}
复制代码

src/pages/api/posts 目录下编写了两个简单的测试接口,这个可以直接去代码里面看,也可以忽略,并不影响。

下面新增动态指令:

// src/pages/api/revalidate/[id].js
export default async function revalidateHandler(req, res) {const { id } = req.query// ...省略try {// 更新 demo3await res.revalidate(`/isr/demo3/${id}`)// 返回说明更新指令已发出,并不能说明一定更新成功return res.json({ revalidated: true })} catch (err) {// 更新失败return res.status(500).send('Error revalidating')}
}
复制代码

使用 getStaticPaths 来获取需要动态生成的页面时,大部分场景可能不需要使用接口请求获取列表,且不能请求本工程定义的 api 接口,因为只会在构建过程中执行,使用本工程的 api 会报错,而且这里请求接口,如果需要生成的页面过多,会导致构建很慢,一般直接返回 { paths: [], fallback: 'blocking' } 即可,这样在页面 首次访问(首个访问页面的用户进行首次访问时会比较慢) 或者被下发 revalidate指令 时就可以生成页面内容。可以打开 demo3,或者本地打开 http://localhost:3000/isr/demo3/[id] 试一下,把 [id] 替换成什么数字都可以。

注意事项:

  • 动态路由主要需要注意 fallback ,默认为 false ,这时任何未使用 paths 字段返回的路径都将会显示 404 页面,blocking 则允许传入其他动态路由参数,一般使用 blocking
  • 构建指令并不一定需要动态指令,也可以全部页面共用一个 revalidateHandler ,使用 query 参数来控制,根据具体情况使用即可,但必须注意安全性问题。

部署遇到的问题和解决方法

现在主要遇到多进程的疑惑和多服务器负载均衡的问题,后续有更多问题会更新到本篇文章,欢迎大家关注➕点赞

Next.js性能优化之ISR渲染入门和原理探索相关推荐

  1. Node.js性能优化

    你不知道的Node.js性能优化 - 知乎 https://zhuanlan.zhihu.com/p/50055740 你不知道的Node.js性能优化 "当我第一次知道要写这篇文章的时候, ...

  2. 【Android 性能优化】布局渲染优化 ( CPU 渲染优化 | 减少布局的嵌套 | 测量布局绘制时间 | OnFrameMetricsAvailableListener | 布局渲染优化总结 )

    文章目录 一. 减少布局嵌套 二. 布局渲染时间测量 1. FrameMetrics 使用流程 2. FrameMetrics 参数解析 3. FrameMetrics 代码示例 三. 布局渲染优化总 ...

  3. 【Android 性能优化】布局渲染优化 ( GPU 过度绘制优化总结 | CPU 渲染过程 | Layout Inspector 工具 | View Tree 分析 | 布局组件层级分析 )

    文章目录 一. GPU 过度绘制优化总结 二. CPU 渲染过程 三. CPU 渲染性能调试工具 Layout Inspector 四. Layout Inspector 组件树 DecorView ...

  4. 【Android 性能优化】布局渲染优化 ( 过渡绘制 | 背景设置产生的过度绘制 | Android 系统的渲染优化 | 自定义布局渲染优化 )

    文章目录 一. 背景设置产生的过度绘制 二. Android 系统的渲染优化 1. 透明组件数据传递 2. GPU 存储机制 3. Android 7.0 之后的优化机制 三. 自定义布局渲染优化 一 ...

  5. 你不知道的Node.js性能优化,读了之后水平直线上升

    本文由云+社区发表 "当我第一次知道要这篇文章的时候,其实我是拒绝的,因为我觉得,你不能叫我写马上就写,我要有干货才行,写一些老生常谈的然后加上好多特技,那个 Node.js 性能啊好像 D ...

  6. js性能优化--学习笔记

    <高性能网站建设进阶指南>: 1.使用局部变量,避免深入作用域查找,局部变量是读写速度最快的:把函数中使用次数超过一次的对象属性和数组存储为局部变量是一个好方法:比如for循环中的.len ...

  7. 【前端性能优化】浏览器渲染原理与性能优化

    目录 1. 浏览器渲染基本步骤 2. 构建DOM树.CSSOM树 3. 构建渲染树 4. 计算渲染树的布局 5. 将布局渲染到屏幕上 6. 渲染优化 1. 浏览器渲染基本步骤 浏览器主要有以下步骤: ...

  8. JS性能优化之文档碎片-document.createDocumentFragment

    讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么.          在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面就会更新并反映出这个变化,对于少量的更新 ...

  9. Android性能优化最佳实践,源码+原理+手写框架

    前言 众所周知,Android是一个基于Linux实现的操作系统.但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别. 所以An ...

最新文章

  1. IDEA键盘突然失去响应
  2. 亚马逊马超:如何使用DGL进行大规模图神经网络训练?
  3. 阿里云服务器买了,如何建站呀?
  4. hdc和hwnd的区别
  5. 高斯混合模型GMM的理解
  6. 三、“涤纶纤维和棉纤维两组分纤维在涤/棉混纺织物燃烧过程中有着明显的物理相互作用和化学相互作用”,解释这两种作用。
  7. Linux 指令的分类 (man page 可查看)
  8. kotlin半生对象_Kotlin程序| 随播对象特征
  9. vue动态引入外部CDN导致线上项目页面无法显示 - 看了不亏
  10. linux的进程/线程/协程系列1:进程到协程的演化
  11. mac系统安装npm所遇问题
  12. http://www.zhihu.com/question/24896283
  13. 在 Linux 下搭建 Java 开发环境
  14. 单片微型计算机第三版课后习题答案,单片微型计算机原理与应用_课后习题答案_山东理工.docx...
  15. 说说 JavaEye 网站架构
  16. Vue3 Composition API教程
  17. 计算机简史:你想不通去脉 是因为你不了解来龙
  18. 剑指 Offer 04. 二维数组中的查找
  19. windows在注册表中删除了HKEY_CLASSES_ROOT\.exe导致无法打开.exe文件
  20. Excel VBA获取选择文件的文件名称

热门文章

  1. 欧拉函数定理及其性质
  2. springCloud笔记——微服务介绍
  3. 微信小程序跳转到H5网页
  4. 电脑键盘equals在哪个位置_学习第二步equals;equals;电脑键盘使用--快捷键大全
  5. 什么级别的企业可以进行数字化转型?
  6. 【Kafka】Kafka为什么快?
  7. SystemUi概述
  8. POJ 1417 True Liars(带权并查集+DP)
  9. 在JDBC中使用预编译Statement 以及它的优点
  10. 电路及电路设计经验技巧大合集