前言
一谈到 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 是怎么工作的
- 服务器请求后端接口
- 服务器渲染 html
- 服务器发送 **完整的 html **
- 浏览器解析 html, 请求
js css
图片,字体等静态资源。

它的问题又在哪?
步骤1,2,3,4是完全串行的,步骤1请求后端接口和步骤4其实可以并行进行,因为请求到达服务器时候,理论上我们就可以把这个页面需要的静态资源下发了。更大的问题是我们要在服务器等待页面所有的后端接口都返回,如果有一个后端接口很慢,它会推迟页面整体的渲染时间
流式渲染的目的就是解决常规 SSR 的问题。
流,顾名思义,就是把内容一部分一部分,像水一样流动出去,其实也就是在浏览器和服务器之间建立一个通道,持续传输 html
流式渲染的步骤如下
- 服务器先发送 head ,将
js css
字体,图片等静态资源下发 - 服务器请求快速的后端接口,对于慢的后端接口,直接把它相关的页面部分渲染成为骨架屏
- 服务器发送 html(含有骨架屏)
- 慢的接口返回之后,再将慢接口相关的页面部分渲染出来,发送一个
script
脚本来替换骨架屏

效果如下:

3. SPA回退
SPA回退是干什么的呢?
想象一个电商场景,一到 618,双十一这样的节日,页面的流量将会暴涨。
而我们的 SSR 服务器其实挺吃资源的,那么流量太大呢,很容易就把服务器打挂了。
所以这个时候我们就需要 SPA 回退了,也就是让服务器只发送一个空的 html 骨架,然后再让 js 去加载页面的内容,它能够大大提升服务器的吞吐量。
结语
讲了这么多,好像还剩下一个最大的问题:
怎么实现呢?
遗憾的是,像Next.js这样的框架中,并不支持对 css 模块的关键性 css提取,也不支持 SPA 回退, 只有流式渲染这个功能,会在 服务器组件
中捆绑支持,而 服务器组件
又是一个特别改变大家对 React 心智的新功能🤬🤬🤬。
不过对于那些使用自定义 webpack
来进行 ssr 的同学来说,好消息来啦!
我写了一个完全支持上述三个功能的 React 流式渲染 SSR框架,直接支持 React18 React19
。
同时,我也实现了一个对于 React17
的例子
你可以直接参照我的做法来给公司的项目接入流式渲染,性能优化的点这不就来了吗🔥🔥🔥