热门前端 SSR 框架性能 PK!React 倒数第一?

大家好,我是 ConardLi

随着 Web 技术的蓬勃发展,服务器端渲染(SSR)逐渐成为了当下前端页面追求高性能的主流技术,用 SSR 构建的项目往往首屏渲染会非常丝滑。但是 SSR 渲染也有可能带来一些其他的性能问题。

我之前做过的很多项目都围绕调试 Node.js 性能问题展开。在这些场景中,导致性能问题的罪魁祸首基本上都是 SSRSSR 是一种占用 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> 元素:

5922c988ee189b981db1c5ef32e0b14f.png

FastifyVite 集成设置是检验各种框架 SSR 性能的理想测试平台。

下面,我们将实现执行 SSR 所需的最少样板代码,并比较五大前端库的性能:React、Vue、Solid、SveltePreact

同时,我们还测试了 fastify-html(一个 Fastify 封装的 ghtml)和 ejs 通过 @fastify/view 提供的简单替代方案。

我们本次的测试不考虑像 Next.js、AstroQwik 等工具,以及其他完整的框架,因为它们不提供单独的渲染方法。

对于基于 @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-htmlejs 测试,它们不需要 Vite

下面是包含所有示例的仓库:https://github.com/platformatic/ssr-performance-showdown

保证一致性

我们确保所有示例都具有相同的特征:

  • 不使用客户端的响应式特性;

  • 所有样式绑定都使用模板字面量,除非对于具体框架不合适,例如 React 和 Solid;

  • xy 值都使用 toFixed(2) 创建;

  • 除文档壳中的 <style> 标签外,不使用其他 <style> 标签。

测试是在 2020 款 MacBook Air M1 上运行的,配置为 8GB RAM,操作系统为 macOS Ventura,Node.js 版本为 v22。

fastify-html

1eb169dd64c8b0625b5e54c5fc6d3f8f.png

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

d1aa0045e9c01c5e288e6e17f1d62a2f.png

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

5da202a4468222c3c6c51cf977f3d49b.png

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

2e5c330ba174ad347c5d877010abe159.png

用于同步服务器端渲染的 Vue APIrenderToString()

import { renderToString } from 'vue/server-renderer'

// ...

await server.register(FastifyVite, { 
  async createRenderFunction ({ createApp }) {
    return async () => ({
      element: await renderToString(createApp())
    })
  }
})

Svelte

ebed5e55fd2350b5999cf60d234998bc.png

位居第三的是 Svelte 5(仍是 pre-release 版本)每秒发送高达 968 个请求,考虑到其丰富的功能集,这个表现还是相当不错的。

Svelte 拥有自己的非 JSX 模板语法,并且其引擎非常高效,如果你需要一个具有成熟库生态系统的框架并且不想在 SSR 性能上妥协,那么它也是一个绝佳的选择。

a6217666b791ac99919796bf162f3dde.png

用于服务器端渲染的 Svelte API 是来自 Svelte 5render()

await server.register(FastifyVite, {
    root: import.meta.url,
    createRenderFunction ({ Page }) {
      return () => {
        const { body: element } = render(Page)
        return { element }
      }
    }
  })

注意,render() 函数还会返回 headbody 属性。

Solid

af872120f36e58df2af4d0ea5b0728c3.jpeg

排名第四的是 SolidJS,每秒处理 907 个请求。它仅以微小的差距落后于 Svelte

Solid 是一个非常有前景的 React 替代品,但其生态系统仍在成长中。

我在测试的时候注意到 SolidJS 在其 hydration 过程中使用 ID 其实会导致一些性能问题。对比 VueSolid 生成的标记如下:

<!-- 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 客户端功能)框架的表现。

bc32ea0855a76a5be0c7501cba3c4bdc.png

在样板代码中,我们使用了 @fastify/vitecreateRenderFunction 钩子来捕获 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

66860e2554c82bb029f272cf3945ba95.jpeg

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

9688255e5a6296cb8c3bc82593703cc8.png

用于同步服务器端渲染的 Preact APIrenderToString()

import { renderToString } from 'preact-render-to-string'


// ...

await server.register(FastifyVite, {
  root: import.meta.url,
  createRenderFunction ({ createApp }) {
    return () => {
      return {
        element: renderToString(createApp())
      }
    }
  }
})

React

0c937ebdba12fceaa1b55ea8cd35de16.png

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

c018f8b114757e63c35779b6fd3a9997.png

用于同步服务器端渲染的 React APIrenderToString()

import { renderToString } from 'react-dom/server'

// ...

await server.register(FastifyVite, {
  root: import.meta.url,
  createRenderFunction ({ createApp }) {
    return () => {
      return {
        element: renderToString(createApp())
      }
    }
  }
})

总结

37990e8382a675aaee9a11d7c790a583.png
  • 参考:https://blog.platformatic.dev/ssr-performance-showdown

最后

抖音前端架构团队目前放出不少新的 HC ,又看起会的小伙伴可以看看这篇文章:抖音前端架构团队正在寻找人才!FE/Client/Server/QA,25 届校招同学可以直接用内推码:DRZUM5Z,或者加我微信联系。

如果你想加入高质量前端交流群,或者你有任何其他事情想和我交流也可以添加我的个人微信 ConardLi

点赞在看是最大的支持⬇️❤️⬇️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值