热门前端 SSR 框架性能大比拼:React 倒数第一?
来源于code秘密花园 ,作者ConardLi
Web 技术的蓬勃发展,服务器端渲染(SSR)逐渐成为了当下前端页面追求高性能的主流技术,用 SSR 构建的项目往往首屏渲染会非常丝滑。但是 SSR 渲染也有可能带来一些其他的性能问题。
我之前做过的很多项目都围绕调试 Node.js 性能问题展开。在这些场景中,导致性能问题的罪魁祸首基本上都是 SSR。SSR 是一种占用 CPU 的活动,很容易成为阻塞 Node.js 事件循环的主要原因。
所以,我对当今最流行的前端库在 SSR 性能方面的现状进行了测试。为此,我构建了一个包含大量元素的样本网站,然后捕获每个库的性能表现。
因此,我们请一个 LLM 编写了一些代码,在容器中使用 div 元素作为 10x10 像素的瓷砖来绘制螺旋:
<script>
const wrapper = document.getElementById('wrapper')
const width = 960
const height = 720
const cellSize = 5
function drawSpiral() {
  let centerX = width / 2
  let centerY = height / 2
  let angle = 0
  let radius = 0
  const step = cellSize
  while (radius < Math.min(width, height) / 2) {
    let x = centerX + Math.cos(angle) * radius
    let y = centerY + Math.sin(angle) * radius
    if (x >= 0 && x <= width - cellSize && y >= 0 && y <= height - cellSize)
    {
      const tile = document.createElement('div')
      tile.className = 'tile'
      tile.style.left = `${x}px`
      tile.style.top = `${y}px`
      wrapper.appendChild(tile)
    }
    angle += 0.2
    radius += step * 0.015
  }
}
drawSpiral()
</script>
随后,我们要求它使用我们计划测试的所有库创建相应版本,并将实现适配为使用每个库的渲染引擎,而不是依赖于原始示例的 DOM 方法。
这是我们的样本文档的样子,包括所有的 2398 个 <div> 元素:

Fastify 与 Vite 集成设置是检验各种框架 SSR 性能的理想测试平台。
下面,我们将实现执行 SSR 所需的最少样板代码,并比较五大前端库的性能:React、Vue、Solid、Svelte 和 Preact。
同时,我们还测试了 fastify-html(一个 Fastify 封装的 ghtml)和 ejs 通过 @fastify/view 提供的简单替代方案。
我们本次的测试不考虑像 Next.js、Astro 和 Qwik 等工具,以及其他完整的框架,因为它们不提供单独的渲染方法。
对于基于 @fastify/vite 的测试,我们使用了如下所示的样板代码:
import Fastify from 'fastify'
import FastifyVite from '@fastify/vite'
const server = Fastify()
// 注册 FastifyVite 插件
await server.register(FastifyVite, /* options */)
// 等待 Vite 准备就绪
await server.vite.ready()
// 监听 3000 端口
await server.listen({ port: 3000 })
所有测试都是在生产构建后运行的,也就是说,在运行 vite build 之后。
唯一的例外是 fastify-html 和 ejs 测试,它们不需要 Vite。
下面是包含所有示例的仓库:https://github.com/platformatic/ssr-performance-showdown
保证一致性
我们确保所有示例都具有相同的特征:
- 
不使用客户端的响应式特性;  - 
所有样式绑定都使用模板字面量,除非对于具体框架不合适,例如 React 和 Solid;  - 
x和y值都使用toFixed(2)创建; - 
除文档壳中的 <style>标签外,不使用其他<style>标签。 
测试是在 2020 款 MacBook Air M1 上运行的,配置为 8GB RAM,操作系统为 macOS Ventura,Node.js 版本为 v22。
fastify-html

我们首先介绍一个特例:fastify-html,这是一个封装了 ghtml 的 Fastify 插件,每秒可以处理 1088 个请求。就像前面所说的,这种设置不同于其他设置,因为它不需要 Vite,因为不需要特殊语法或转换。

fastify-html 被添加到测试中作为一个基准。由于它只是一个简单的 HTML 模板库的封装,没有其他库的高级特性,所以它与其他库的对比并不是非常恰当。基于它的简单特性,我们已经预期它会表现得更好,并想看看其他功能齐全的库与它相比会差多少。
下面是我们使用的样板代码 — createHtmlFunction(模拟 @fastify/vite)用于注册渲染文档壳的布局函数:
import Fastify from 'fastify'
import fastifyHtml from 'fastify-html'
import { createHtmlFunction } from './client/index.js'
const server = Fastify()
// 注册 fastify-html 插件
await server.register(fastifyHtml)
// 增加布局函数
server.addLayout(createHtmlFunction(server))
作为参考,我们还添加了一个使用旧版 EJS (基于 @fastify/view)的测试,它每秒可以处理 443 个请求。
Vue

位居第二,如果您想要出色的 SSR 性能并想要一个真正全面的库生态系统,Vue 每秒可处理 1028 个请求,它可能是最好的选择。

用于同步服务器端渲染的 Vue API 是 renderToString():
import { renderToString } from 'vue/server-renderer'
// ...
await server.register(FastifyVite, { 
  async createRenderFunction ({ createApp }) {
    return async () => ({
      element: await renderToString(createApp())
    })
  }
})
Svelte

位居第三的是 Svelte 5(仍是 pre-release 版本)每秒发送高达 968 个请求,考虑到其丰富的功能集,这个表现还是相当不错的。
Svelte 拥有自己的非 JSX 模板语法,并且其引擎非常高效,如果你需要一个具有成熟库生态系统的框架并且不想在 SSR 性能上妥协,那么它也是一个绝佳的选择。

用于服务器端渲染的 Svelte API 是来自 Svelte 5 的 render()。
  await server.register(FastifyVite, {
    root: import.meta.url,
    createRenderFunction ({ Page }) {
      return () => {
        const { body: element } = render(Page)
        return { element }
      }
    }
  })
注意,render() 函数还会返回 head 和 body 属性。
Solid

排名第四的是 SolidJS,每秒处理 907 个请求。它仅以微小的差距落后于 Svelte。
Solid 是一个非常有前景的 React 替代品,但其生态系统仍在成长中。
我在测试的时候注意到 SolidJS 在其 hydration 过程中使用 ID 其实会导致一些性能问题。对比 Vue 和 Solid 生成的标记如下:
<!-- Vue 生成的标记 -->
<div class="tile" style="left: 196.42px; top: 581.77px"></div>
<!-- Solid 生成的标记 -->
<div data-hk=1c2397 class="tile" style="left: 196.42px; top: 581.77px"></div>
这意味着性能开销很大一部分来自于这些需要传输的额外片段。不过,我们希望验证的也正是这一点:在常见、真实的使用情况下(启用 hydration 客户端功能)框架的表现。

在样板代码中,我们使用了 @fastify/vite 的 createRenderFunction 钩子来捕获 Solid 组件函数(createApp):
import { renderToString } from 'solid-js/web'
// ...
await server.register(FastifyVite, {
  root: import.meta.url,
  createRenderFunction ({ createApp }) {
    return () => {
      return {
        element: await renderToString(createApp)
      }
    }
  }
})
Preact

React 的 “弟弟” Preact 排名第五,每秒处理 717 个请求。尽管 Preact 与 React 非常相似,但很多细节上的差异使得 Preact 更快且更轻量。

用于同步服务器端渲染的 Preact API 是 renderToString():
import { renderToString } from 'preact-render-to-string'
// ...
await server.register(FastifyVite, {
  root: import.meta.url,
  createRenderFunction ({ createApp }) {
    return () => {
      return {
        element: renderToString(createApp())
      }
    }
  }
})
React

React 19 RC 排名第六,每秒处理 572 个请求。

用于同步服务器端渲染的 React API 是 renderToString():
import { renderToString } from 'react-dom/server'
// ...
await server.register(FastifyVite, {
  root: import.meta.url,
  createRenderFunction ({ createApp }) {
    return () => {
      return {
        element: renderToString(createApp())
      }
    }
  }
})
总结

- 
参考:https://blog.platformatic.dev/ssr-performance-showdown  






