跳到正文
Hacker News· luu·· 4 小时前精选AI 评分67

JavaScript 框架的 AI 成本:Opus 5.5 多框架基准评测

The AI cost of JavaScript frameworks

AI 导读

作者让 Opus 5.5 使用不同 JavaScript 框架与元框架构建同一款 Pokémon 应用,并比较成本、耗时、代码量与 Lighthouse 表现。

推荐理由

作者用统一应用规格比较多款 JavaScript 框架方案的构建成本、耗时、代码量和 Lighthouse 表现,为智能体编程选型提供横向数据。

正文 · AI 翻译

我在 Web 开发社区中最反感的一点是,我们做出的许多决定都以感觉为准。框架“快如闪电”,工具会“让你的开发效率飙升”,正确的抽象能同时惠及开发者和用户(DX 与 UX 二合一!)。所有这些激动人心的夸耀通常都没有任何证据。Alex Russell 曾称之为“前端失去的十年”,Baldur Bjarnason 曾称编程是一种流行文化,而我则一直试图在博客中用事实和基准测试来抵制这种趋势(至少我已经尽力了)。

很长一段时间以来,我们社区对证据的排斥似乎源于追求更高 DX。换句话说,开发者只是喜欢在 React 中输入 JSX 时的手感,而推广 React 或相关工具的论点(“虚拟 DOM 很快!”)在很大程度上都是事后为最初直觉找的合理化理由。(Jonathan Haidt很可能会认同这一评价。)

但是在智能体时代,开发者不再亲手编写代码,因此不再那么受 DX 制约,我仍然看到大家继续凭感觉行事(这不只是“凭感觉编程”的意思)。前端被重写,只因为某个框架“更适合智能体”。(有人想到问一问智能体吗?)最终得到的代码“更快”,智能体犯的“错误更少”,等等。这些说法 Again 往往毫无证据。每当我读到其中一种说法时,都会想:“基准测试在哪里?”

不过,我好奇了起来,于是写了一个基准测试。这个基准测试的结构相当简单明了:

  • 使用PokéAPI设计一个小型但并不简单的宝可梦应用(那些一直关注我的 工作的人不会感到意外)
  • 指示智能体使用特定的 JavaScript 框架或元框架(例如 Next.js、Nuxt、SvelteKit 等)构建该应用
  • 根据规范符合性测试套件测试输出结果(以防止智能体作弊)
  • 从开发时间、消耗的 token 数量、代码行数,以及Lighthouse中的性能和无障碍评分等方面评估结果

这个应用相当简单:你会看到一个宝可梦列表,其中包含它们的属性、招式以及来自各种游戏的数据。你可以搜索、筛选等等:

Screenshot of a Pokémon web app with various monsters listed, their types, etc.

首先,我得说明几点注意事项:这不是一项完美的实验。这个应用只代表一个正模标本——它是一个相对以内容为主、交互性较少的网站,更接近 Wikipedia,而不是 Figma、GMail 等。尽管如此,我总得选择某个应用来构建,而这个应用至少从高层角度来看容易理解,规格制定和测试也相对直接。

为了尽量让各个框架或元框架站在同一起跑线上,我让它们各自使用该工具的官方 MCP 或 skills。我不想招来这样的指责:“这个框架在训练数据中的体现不够充分,所以这是我们构建 MCP 的原因,我们希望人们使用它”等等。我也没有过度规定具体应采用的架构(例如 SSR 还是 CSR),也没有要求该应用必须尽可能高性能。我只是以高努力档把规格说明交给了 Opus 5.5,并告诉它使用指定框架的最新版本。每个智能体都在沙箱中运行,而且不知道它之后会在 Lighthouse 的性能和无障碍方面接受测试,尽管它已经获得了符合性测试套件。

另外,我也没有强制规定任何 CSS 方法论,只要求避免使用 Tailwind、Bootstrap 或其他组件框架,并基于一组规范令牌进行设计,以确保视觉一致性。我不想让这次测试全都围绕 CSS,而是聚焦于 JS 框架、CSR 与 SSR、SPA 与 MPA 等。最终生成的应用是基于无障碍树而非原始 DOM 的符合性测试套件进行测试,以避免强制规定任何特定的 HTML 结构——有一个框架使用了依赖 Shadow DOM 的 Web Components,因此必须这样做。

我从这次实验中学到的一件事是,我所衡量的一切都具有很强的随机性。LLM 是随机词生成器,所以有时你会得到一个较低的骰子点数,代理就会偏离到某个奇怪的方向,浪费大量 token,这并不是框架或其工具的过错。Lighthouse 本身也可能给出不一致的结果:最大内容绘制(LCP)可能认定某张图片是“最大”元素,下次运行时却又选择另一张。要进行完全公平的实验很难,但运行多次迭代确实有所帮助,我也努力如实反映这些差异。

闲话扯完,现在来看结果。

结果

我将从多个维度进行拆分:构建成本、质量和可维护性。对于每个框架/元框架,我取了 5 次运行的中位数:也就是说,我用同一个框架构建了 5 次相同的应用。(是的,如今利用编程代理可以做这种有点疯狂的事情。)

例外是 Angular,它有两次未能创建可正常运行的应用,因此我改用 3 次迭代。(下文会对此作出解释。)

成本

每次运行的成本,以美元计
框架 成本 ($)
Hono+htmx $2.67 (2.23–3.19)
Astro $2.76 (2.16–3.63)
Nuxt $2.82 (2.39–4.25)
Vanilla $2.98 (2.24–3.37)
Next.js $3.14 (2.58–4.13)
Qwik $3.27 (2.35–3.80)
TanStack Start $3.54 (3.11–3.96)
SolidStart $3.79 (2.84–4.81)
React Router $3.81 (2.82–3.97)
SvelteKit $4.28 (3.59–4.65)
Gracile+Lit $4.65 (3.92–5.07)
Angular $4.97 (3.19–6.82)

3-5 次运行的中位数。

注意,这些价格直接引自 Claude Code 的 API 定价,并不实际代表我花了多少钱。我使用的是 Max 套餐,因此这只是我每月 $100/month 总费用的一部分。

还要注意波动幅度:React Router 的中位数大约比 Hono+htmx 高 $1,但 React Router 最便宜的版本($2.82)仍比 Hono+htmx 最贵的版本($3.19)便宜。不过,一些可靠的规律确实显现出来了。

最明显的是,SvelteKit、Gracile+Lit 和 Angular 的构建成本高于其他框架。对于 SvelteKit,这可能是因为每次运行时,代理都会阅读 SvelteKit v3 迁移指南,很可能是因为它太新了,尚未进入 Opus 5.5 的训练数据。Gracile 也是如此,代理们不太了解这个框架,因此要么克隆 Gracile 仓库,要么从 node_modules 阅读其编译后的源代码。对于 Angular,这似乎取决于代码总大小(见下文)。

我还应该说明,并非每个代理都使用了 MCP/技能/Claude Code 插件,也不是每个框架都配备了此类工具:

点击查看 AI 工具使用情况

框架 已安装 智能体是否使用?
Angular Angular MCP 和 Angular 智能体技能 MCP 在 3/5 次运行中使用,技能工具在 4/5 次运行中使用,技能文件在 2/5 次运行中使用
Astro Astro MCP 服务器 MCP 在 2/5 次运行中使用
Gracile+Lit N/A N/A
Hono+htmx Hono 技能 技能工具在 5/5 次运行中使用
Next.js next-devtools-mcp 和 next-dev-loop 技能 技能文件在 1/5 次运行中使用,AGENTS.md 在 2/5 次运行中使用
Nuxt Nuxt MCP N/A
Qwik N/A N/A
React Router React Router 技能 技能工具在 5/5 次运行中使用,技能文件在 5/5 次运行中使用
SolidStart N/A N/A
SvelteKit Svelte Claude Code 插件 5/5 次运行中均使用 MCP
TanStack Start N/A 2/5 次运行中发现技能文件
Vanilla N/A N/A

例如,TanStack Start 的智能体只有 2/5 次运行发现了它的技能文件(这些文件随项目的 node_modules 一同提供)。对于 Nuxt,智能体从未使用 MCP。对于 Next.js,智能体只有 1/5 次运行读取了技能文件,2/5 次运行读取了 AGENTS.md。结果分布范围很广,这再次指向 LLM 的随机性本质。不过,我仍认为这是一项有效的实验,因为我怀疑很少有 vibe 编程者会用全大写字母对智能体大喊大叫,让它们使用 MCP(而且很可能有不少人干脆完全不用它)。

另外,如果你想知道我是怎么想到 Hono+htmx 的:本质上,我想以某种方式体现 htmx,而 Hono 看起来是个不错的搭配(即充当 Rails/Django 等框架的 JavaScript 替代方案)。至于 Gracile+Lit,我想通过其标杆框架 Lit 来体现 Web 组件,而当时我对 Gracile 的了解仅限于它是一个元框架。如果有更合适的候选方案,那就很适合用于后续实验。

对于“Vanilla”,请注意,智能体被告知要使用“原生 JavaScript 或 TypeScript 配合 Vite,不使用 UI 框架”。令人意外的是,这种方式的表现不及多个框架,这似乎说明,拥有某种框架可能比没有框架更好(或者至少比直接使用 Vite 更好)。选择 Vite,是因为它是许多框架的基础,并且代表了相当标准、与具体框架无关的 JavaScript 基础设施。

总耗时和 token 数量与成本相当吻合:

每次运行耗时
框架 耗时(分钟)
Astro 8.2 min (6.2–9.7)
Hono+htmx 8.6 min (5.9–9.6)
Nuxt 9.0 min (8.0–17.6)
Vanilla 10.1 min (6.9–18.6)
TanStack Start 10.3 min (8.3–13.9)
Qwik 10.7 min (7.1–12.9)
SvelteKit 10.8 min (8.6–13.8)
Next.js 10.9 min (8.3–11.9)
SolidStart 11.2 min (8.2–15.9)
Gracile+Lit 12.1 min (10.8–12.9)
React Router 12.5 min (8.4–13.6)
Angular 17.2 min (9.8–23.7)

从智能体视角测得的实际耗时。3-5 次运行的中位数。

每次运行的 token 数(以百万计)
框架 Token 数(M)
Nuxt 3.85 M (3.16–8.18)
Astro 4.13 M (3.03–7.57)
Hono+htmx 4.26 M (3.40–5.61)
Next.js 4.99 M (3.54–8.82)
Vanilla 5.00 M (3.44–6.32)
Qwik 5.64 M (3.41–7.03)
TanStack Start 7.07 M (5.85–7.99)
SolidStart 7.13 M (5.02–10.07)
React Router 7.58 M (4.55–8.18)
SvelteKit 8.53 M (6.79–9.29)
Gracile+Lit 9.04 M (7.36–11.10)
Angular 9.76 M (4.92–14.94)

缓存读取、未缓存的输入和输出。3-5 次运行的中位数。

顺便说一下,token 数量是由 Claude Code 报告的。持续时间只是总挂钟时间。

质量

当然,因为我认为最终效果很重要,我还使用 Lighthouse 对这些智能体进行了测试。作为软件开发者,我认为我们不仅要以低成本构建东西,还要确保它们性能出色且具备良好的可访问性。

首先,我应该说明关于我的基准测试的两点注意事项:

  • 这款 Pokémon 应用规模很小,并不能代表大型、复杂的真实世界应用。
  • Lighthouse 和 Core Web Vitals 总体上对 SPA 不利,因此 MPA 框架几乎必然会在总阻塞时间 (TBT)、JavaScript 字节数等指标上胜出。我考虑过添加一些应用内性能指标,但后来放弃了,因为这会增加基准测试的复杂性和波动性,而且这些指标将由我自行编造,而不是采用行业标准。

为简单起见,我只报告首页的分数。还有其他页面,但它们的得分相当接近,或者在 LCP 等指标上出现剧烈波动,因此将它们纳入只会让事情更加复杂。

简而言之:Lighthouse 的性能、可访问性和 CLS (累积布局偏移) 分数几乎已经饱和,而且不同框架之间的差异不大:

框架 CLS 性能 可访问性
Angular 0.000 98 100
Astro 0.000 100 100
Gracile+Lit 0.000 100 100
Hono+htmx 0.000 100 100
Next.js 0.000 99 100
Nuxt 0.000 100 100
Qwik 0.000 100 100
React Router 0.000 100 100
SolidStart 0.000 100 100
SvelteKit 0.000 100 100
TanStack Start 0.000 99 100
Vanilla 0.111 97 100

因此,要获得一些更有意思的数据,我们需要查看其他指标。最可靠的指标是传输的 JavaScript 字节数,它在很大程度上反映了客户端框架的大小:

传输的 JavaScript 字节数
框架 JS (KiB)
Astro 0 KiB (0–1)
Vanilla 7 KiB (6–7)
Gracile+Lit 8 KiB (1–13)
Hono+htmx 18 KiB (18–18)
SvelteKit 36 KiB (35–36)
SolidStart 40 KiB (38–40)
Qwik 40 KiB (40–41)
Nuxt 84 KiB (83–85)
Angular 93 KiB (81–102)
React Router 115 KiB (115–119)
TanStack Start 118 KiB (112–119)
Next.js 142 KiB (136–143)

由 Lighthouse 报告。3-5 个应用的中位数。

总阻塞时间 (TBT) 很有意思,因为它并非完全是 JavaScript 字节数的函数;其中一些应用在主线程上耗费的时间比其他应用更长。尽管如此,这些数字都相当小:

总阻塞时间 (TBT)
框架 TBT (ms)
Astro 0 ms (0–0)
Gracile+Lit 0 ms (0–0)
Hono+htmx 0 ms (0–0)
SvelteKit 0 ms (0–1)
Vanilla 0 ms (0–0)
TanStack Start 1 ms (0–36)
Qwik 3 ms (0–30)
SolidStart 11 ms (0–12)
React Router 17 ms (0–52)
Nuxt 40 ms (14–54)
Next.js 71 ms (66–121)
Angular 103 ms (93–123)

按照 Lighthouse 的报告,采用模拟移动端限速。每个应用进行 5 次 Lighthouse 运行并取中位数,然后再对 3-5 个应用取中位数。

由于前述指标本身存在的问题,最大内容绘制(LCP)的波动性很高。不过,正如预期,它总体上确实会惩罚 SPA,并奖励 MPA:

最大内容绘制(LCP)
框架 LCP (ms)
Astro 810 ms (792–1662)
Gracile+Lit 956 ms (796–980)
Hono+htmx 978 ms (977–1662)
Qwik 979 ms (954–1278)
SvelteKit 1130 ms (1129–1131)
SolidStart 1204 ms (1203–1278)
Vanilla 1667 ms (764–1676)
Nuxt 1727 ms (1427–1844)
React Router 1729 ms (1729–1770)
Angular 1954 ms (1351–1955)
TanStack Start 2028 ms (1805–2104)
Next.js 2090 ms (1028–2187)

按照 Lighthouse 的报告,采用模拟移动端限速。每个应用进行 5 次 Lighthouse 运行并取中位数,然后再对 3-5 个应用取中位数。

请注意,Next.js 数字的巨大波动可以解释为:一个应用避免了延迟加载图片,而另外四个没有这么做。Astro 和 Vanilla 也出现了类似的波动,似乎是由优先级处理或 LCP 的特殊问题造成的。

不过总体而言,该指标往往会奖励能够快速借助 HTML 完成绘制的 MPA,而不是那些要等客户端 JavaScript 运行后才绘制的 SPA(期间还可能经历多次受到限速影响的往返请求,以获取 Pokémon 数据),这并不令人意外。忽略方差,这里的 LCP 主要取决于 JavaScript 包体积。(Qwik 是一个值得注意的例外:从技术上说,它是一个使用客户端路由的 SPA,但其得分与 MPA 框架相当。)

不过请注意,一些框架在更侧重客户端还是服务器方面存在差异,甚至是否使用客户端框架也不尽相同。例如,Gracile 智能体甚至在两次运行中都没有使用 Lit。对于“Vanilla”,智能体拥有完全的自由裁量权(唯一限制是被要求使用 Vite),但仍然连续 5 次都选择构建 SPA。

SSR 与 CSR 的取舍也是 Angular 出现问题的部分原因:在 2 次运行中,智能体构建了一个会拒绝非 localhost 请求的 SSR 应用(尽管说明中明确指出该应用将位于反向代理之后)。我认为这确实属于未遵守规范,因此决定不重新运行这 2 次失败的迭代。

可维护性

衡量可维护性的方法很多(包括循环复杂度之类的复杂指标),但我决定保持简单,报告一个久经考验的指标:代码总行数。

代码行数
框架 代码行数
Hono+htmx 1370 (1264–1489)
Nuxt 1415 (1382–1458)
Astro 1461 (1379–1503)
Vanilla 1492 (1437–1742)
Next.js 1539 (1452–1627)
React Router 1564 (1421–1621)
SvelteKit 1611 (1503–1816)
SolidStart 1630 (1528–1894)
TanStack Start 1697 (1506–1771)
Gracile+Lit 1717 (1412–1748)
Qwik 1744 (1646–1827)
Angular 2009 (2003–2051)

源文件中非空行的数量。3-5 个应用的中位数。

对我来说,这是其中较有意思的图表之一。从某种意义上说,框架的核心目的就在于减少你需要维护的代码量。用 Rich Harris 那句令人难忘的话说:

“框架不是用来组织代码的工具;它们是用来组织思维的工具。”

– Rich Harris,“重新思考响应式”

在智能体时代,这番话更显切中要害,因为代码规模最终会影响所需的 token 数量,包括未来编辑时所需的数量,这意味着它会直接影响长期维护代码库的实际成本。这并不意味着简练是唯一重要的价值,但看到 Hono+htmx 仅用 ~70% 的代码就能大致完成 Angular 所能做的事情(而且速度也更快),确实很有意思。

结论

你可以通过许多方式改进这个基准测试。例如,可以要求智能体使用 Service Workers 进行离线缓存,或优化宝可梦图片文件,或使用懒加载、fetchpriority、Speculation Rules API 等。尽管我了解许多这类性能优化技巧,但我故意没有引导智能体采用其中任何一种,因为典型的凭感觉编程者不会这么做。

另一方面,只要预先将所有 HTML 文件生成静态资源,这个基准测试就完全可能被作弊绕过(宝可梦的数量终究有限)。我在说明中明确禁止了这种做法,并在测试工具中将其设为一项明确的检查,因为我认为这违背了基准测试的初衷。(我的目标是模拟大量可能动态化的数据,这比纯静态网站更具挑战性。)

你也可以通过增加运行次数、使用不同的编程智能体、尝试不同的投入程度、使用不同的框架或框架的不同版本、分别测试有无 MCP/skills、在 Chromium 之外的更多浏览器中测试、构建不同类型的应用等,来改进我运行这个基准测试的方式。我没有做这些事,主要是为了节省时间。不过,如果有人想自行运行这个基准测试或对它进行衍生开发,它就在 GitHub 上,所以我鼓励你放手去试。我很乐意在这里公布你的结果,以及你可能发现的任何错误或缺陷。

我写这篇文章的目标,并不是要为“我是个凭感觉编程者,该选哪个框架?”这个问题打造一个包打天下的基准测试。本文的重点是证明,在 AI 编程时代,我们仍然可以基于证据而非感觉来做决定。这个基准测试本身大部分也是凭感觉编程完成的,尽管我确实审查过代码,所以如果存在错误,还请将其视为我的问题。我也完全乐见竞争框架尝试在我这里所做的基础上取得更好的成绩。

我也不是想抹黑任何特定框架。在运行基准测试之前,我不知道哪个框架会“获胜”,尽管我确实怀疑 MPA 的表现会优于 SPA(这一点我在本文中一直毫不避讳地承认)。你当然可以把应用做成复杂的仪表板、电子游戏、效率工具等,从而编写一个更偏向 SPA 的基准测试。你也可以选择不同的衡量指标:Lighthouse 和 Core Web Vitals 对 SPA 相当苛刻,因此可以改为衡量应用内的交互性。

我还确信,框架作者 themselves 可以改进各自的框架或 AI 工具,并在这个基准测试中取得更好的表现。例如,我强烈怀疑当前这批 MCP 和技能可能弊大于利。因此,如果这篇文章成了这些框架性能表现的最终定论,我会非常失望。我敢肯定,也有一些新兴框架正迫不及待地想在这样的基准测试中取得好成绩。

至少但愿这篇文章能促使你在这个古怪的新“氛围编程”时代,进一步审视自己对 JavaScript 框架的选择。你选择技术栈时真的以证据为依据吗?还是只是沿用自己已经熟悉的东西?这当然可以理解,尤其是当你仍在亲自阅读、审查和调试代码的情况下。但如果你已经基本全程放手不管,那么对于当年像动物一样逐行编写代码时所作的选择,你如今究竟还有多少依恋?如果你正在重新评估构建软件的整体方式,也许也值得重新审视自己在 npm install 之后输入的东西?随口说说。

就个人而言,除了坚信我们应当努力为用户打造尽可能好的体验之外,我对上文评估过的这些框架没有任何偏爱。我认识并尊重上述许多框架的作者,也知道他们正在为我所倡导的同一价值观不懈努力。我只是希望推动整个社区朝更好的方向发展,打造更优质、更快、更易访问的框架,为下一代 Web 应用提供动力。

补充说明

测试中使用的框架版本(注意,Qwik 在测试过程中发布了一个补丁版本):

框架 版本
Angular @angular/core 22.2.3
Astro astro 7.4.2
Gracile+Lit @gracile/gracile 0.11.0, lit 3.3.3
Hono+htmx hono 4.13.14, htmx.org 2.0.11
Next.js next 16.4.3, react 19.3.1
Nuxt nuxt 4.6.2, vue 3.5.44
Qwik @builder.io/qwik-city 1.20.2(第 1 次迭代),1.20.3(第 2、3、4、5 次迭代),@builder.io/qwik 1.20.2(第 1 次迭代),1.20.3(第 2、3、4、5 次迭代)
React Router react-router 8.5.0, react 19.3.1
SolidStart @solidjs/start 2.1.0, solid-js 1.9.17
SvelteKit @sveltejs/kit 3.1.0, svelte 5.57.2
TanStack Start @tanstack/react-start 1.169.4, react 19.3.1
Vanilla vite 8.3.5

你可以在联邦宇宙上发表评论。

来源:Hacker News · nolanlawson.com