极致的SSR性能优化

前言

一谈到 SSR,大家可能立马想到以下关键词:

  • FCP首屏时间优化
  • SEO友好

现在去实现 SSR 并不难,Next.js / Nuxtjs 为我们提供了良好的上层封装,我们很容易获得上述 SSR 的好处。

但怎么进一步提升 SSR 的性能呢?

嘿嘿,这就是本文要讲的了。我们将会从以下三个模块入手,去将 SSR 性能优化到极致🔥🔥🔥

1. 关键性 CSS

首先解释一下,什么是关键性 CSS。

我们常规的一个 nextjs 项目,假设你使用 css module 的话,它是会将你的 css 抽取成为一个文件,然后去使用 link 标签去加载这个文件的地址,像 mini-css-extract-plugin 就是用来干这个的。

它的问题是什么呢?

chrome 的 web-vitals 团队已经把答案给我们了: 一个字,慢!。这是因为就算你的 html 已经加载完毕了,但如果此时 css 还没有加载好,页面也不会渲染。

有兴趣看这个图完整文章的可以戳这

那此时关键性 CSS 的意思已经呼之欲出了,它就是让我们的 html 不阻塞,直接渲染的 css。它的做法也很简单:不要去将 css 提取到一个文件中,而是直接在我们的页面渲染的时候,将 css 转换为 style 标签,插入到我们的 head 标签中。

最佳实践:将首屏的 css 提取为关键性 css,而次屏的 css 依然使用外链插入,这样可以减少 html体积

2. 流式渲染

流式渲染可以显着减少 TTFB(第一个字节时间)、FCP(首次内容绘制)和 TTI(交互时间)。

首先来看一看常规的 SSR 是怎么工作的

  1. 服务器请求后端接口
  2. 服务器渲染 html
  3. 服务器发送 **完整的 html **
  4. 浏览器解析 html, 请求 js css 图片,字体等静态资源。

它的问题又在哪?

步骤1,2,3,4是完全串行的,步骤1请求后端接口和步骤4其实可以并行进行,因为请求到达服务器时候,理论上我们就可以把这个页面需要的静态资源下发了。更大的问题是我们要在服务器等待页面所有的后端接口都返回,如果有一个后端接口很慢,它会推迟页面整体的渲染时间

流式渲染的目的就是解决常规 SSR 的问题。

流,顾名思义,就是把内容一部分一部分,像水一样流动出去,其实也就是在浏览器和服务器之间建立一个通道,持续传输 html

流式渲染的步骤如下

  1. 服务器先发送 head ,将 js css 字体,图片等静态资源下发
  2. 服务器请求快速的后端接口,对于慢的后端接口,直接把它相关的页面部分渲染成为骨架屏
  3. 服务器发送 html(含有骨架屏)
  4. 慢的接口返回之后,再将慢接口相关的页面部分渲染出来,发送一个 script 脚本来替换骨架屏

效果如下:

3. SPA回退

SPA回退是干什么的呢?

想象一个电商场景,一到 618,双十一这样的节日,页面的流量将会暴涨。

而我们的 SSR 服务器其实挺吃资源的,那么流量太大呢,很容易就把服务器打挂了。

所以这个时候我们就需要 SPA 回退了,也就是让服务器只发送一个空的 html 骨架,然后再让 js 去加载页面的内容,它能够大大提升服务器的吞吐量。

结语

讲了这么多,好像还剩下一个最大的问题:

怎么实现呢?

遗憾的是,像Next.js这样的框架中,并不支持对 css 模块的关键性 css提取,也不支持 SPA 回退, 只有流式渲染这个功能,会在 服务器组件 中捆绑支持,而 服务器组件 又是一个特别改变大家对 React 心智的新功能🤬🤬🤬。

不过对于那些使用自定义 webpack 来进行 ssr 的同学来说,好消息来啦!

我写了一个完全支持上述三个功能的 React 流式渲染 SSR框架,直接支持 React18 React19

同时,我也实现了一个对于 React17例子

你可以直接参照我的做法来给公司的项目接入流式渲染,性能优化的点这不就来了吗🔥🔥🔥

相关推荐
烛阴28 分钟前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小536 分钟前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步
穗余1 小时前
NodeJS全栈开发面试题讲解——P6安全与鉴权
前端·sql·xss
穗余2 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js
航Hang*2 小时前
WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
前端·笔记·edge·less·css3·html5·webstorm
江城开朗的豌豆2 小时前
JavaScript篇:a==0 && a==1 居然能成立?揭秘JS中的"魔法"比较
前端·javascript·面试
江城开朗的豌豆2 小时前
JavaScript篇:setTimeout遇上for循环:为什么总是输出5?如何正确输出0-4?
前端·javascript·面试
橘子味的冰淇淋~3 小时前
npm run build 报错:Some chunks are larger than 500 KB after minification
前端·npm·node.js
QING6183 小时前
Gradle 核心配置属性详解 - 新手指南(二)
android·前端·gradle
普通老人3 小时前
【前端】Vue中实现pdf逐页转图片,图片再逐张提取文字
前端·vue.js·pdf