JavaScript 框架的 AI 成本

Hacker News Top 新闻

摘要

Nolan Lawson 运行了一项基准测试,让 AI 智能体使用不同的 JavaScript 框架构建同一个 Pokémon 应用,测量开发时间、token 消耗、代码体积和 Lighthouse 得分。这篇文章对智能体编程时代中仅凭感觉的框架宣称提出质疑,呼吁拿出证据。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/11/01 09:25

# JavaScript 框架的 AI 成本 原文:https://nolanlawson.com/2026/10/28/the-ai-cost-of-javascript-frameworks/ 我对 Web 开发社区最不满意的一点是:我们做的太多决定都是凭感觉的。框架"极速",工具能"极大地提升"你的开发效率,恰当的抽象层能同时惠及你和用户(开发体验和用户体验一箭双雕!)。这些令人兴奋的吹嘘通常没有任何证据支持。Alex Russell 称之为"前端迷失的十年"(https://www.youtube.com/watch?v=0XwWVjQOmyg),Baldur Bjarnason 称编程是一种流行文化(https://www.baldurbjarnason.com/2022/programming-is-a-pop-culture/),而我一直试图用事实和基准测试来对抗这种趋势(至少在我状态好的时候是这样)。 很长一段时间里,似乎社区对证据的排斥源于对更好开发体验的追求(https://infrequently.org/2018/09/the-developer-experience-bait-and-switch/)。换句话说,开发者就是喜欢在 React 里写 JSX 的*感觉*,而那些推崇 React 或相关工具的论点("虚拟 DOM 很快!")很大程度上只是事后合理化,用来为最初凭直觉做出的选择辩护。(Jonathan Haidt (https://en.wikipedia.org/wiki/The_Righteous_Mind#Key_concepts_and_scholars_discussed) 可能会同意这个判断。) 但在智能体时代,当开发者不再手动编写代码、因此较少受制于开发体验时,我仍然看到凭感觉做事的风气盛行(而且不仅仅是"凭感觉写代码"这层含义)。前端被推倒重写,仅仅因为某个框架"对智能体更友好"。(有人想过问问智能体的意见吗?)生成的代码"更快",智能体"犯的错更少"等等。同样的,这些说法往往没有证据。每当读到这类描述,我都会想:"'基准测试在哪儿?' 于是我产生了好奇心,于是写了一个基准测试。测试的结构相当简单: - 使用 PokéAPI (https://pokeapi.co/) 设计一个小而功能不减的宝可梦应用(关注过我的(https://nolanlawson.com/tag/pokedroid/)作品(https://pocketjavascript.com/blog/2015/11/23/introducing-pokedex-org/)的人不会感到意外) - 指示智能体使用特定的 JavaScript 框架/元框架(如 Next.js、Nuxt、SvelteKit 等)来构建该应用 - 用规范一致性测试套件测试输出(防止智能体作弊) - 从开发时间、消耗的 Token 数、代码行数以及 Lighthouse (https://developer.chrome.com/docs/lighthouse/) 的性能和可访问性评分来评估结果 这个应用相当简单:你有一个宝可梦列表,包含它们的属性、招式以及来自不同游戏的数据。可以搜索、筛选等: > 宝可梦 Web 应用的截图,列出了各种怪物及其属性等。 首先要说明几点注意事项:这不是一个完美的实验。这个应用仅代表一种应用原型(https://jasonformat.com/application-holotypes/)——它是一个偏内容型的网站,交互较少,更接近 Wikipedia 而不是 Figma、GMail 等。话虽如此,我总得选择*某个*应用来构建,而这个应用至少在高层次上易于理解,也相对容易制定规范和进行测试。 为了尽量平衡各框架的条件,我让每个框架/元框架都使用其官方的 MCP 或 skills。我想避免"这个框架在训练数据中覆盖不足,所以我们自建了 MCP,我们期望人们使用它"之类的指责。我也没有过度指定具体的架构(如 SSR 还是 CSR),也没有要求应用必须尽可能高性能。我只是把规范交给 Opus 5.5(高努力模式),并告诉它使用该框架的最新版本。每个智能体在沙箱中运行,并且不知道自己会被 Lighthouse 进行性能和可访问性测试,尽管它被提供了规范一致性测试套件。 请注意,我也没有强制要求任何 CSS 方法论,只是要求避免使用 Tailwind、Bootstrap 或任何其他组件框架,并基于一组规范 token 进行设计以确保一定的视觉一致性。我不想让这个测试变成 CSS 测试,而是要测 JS 框架、CSR 与 SSR、SPA 与 MPA 等。最终产出的应用是基于可访问性树(而非原始 DOM)进行一致性测试的,以避免强制要求特定的 HTML 结构——有一个框架使用了依赖 Shadow DOM 的 Web 组件,所以这是必需的。 从这个实验中学到的一件事是:我测量的所有东西都具有很强的随机性。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 总预算的一部分。 还要注意变异性:React Router 的中位数比 Hono+htmx 高出约 $1,但 React Router 最便宜的一次($2.82)仍然比 Hono+htmx 最贵的一次($3.19)要低。不过,一些稳定的模式确实浮现出来了。 最明显的是,SvelteKit、Gracile+Lit 和 Angular 的构建成本比其他框架更高。对于 SvelteKit,这可能是因为智能体在每次运行时都阅读了 SvelteKit v3 迁移指南 (https://svelte.dev/docs/kit/migrating-to-sveltekit-3),很可能是因为它太新了(https://svelte.dev/blog/sveltekit-3-is-here),尚未出现在 Opus 5.5 的训练数据中。Gracile 也类似,智能体对该框架不太了解,要么克隆了 Gracile 仓库,要么从 `node_modules` 中阅读其编译后的源码。对于 Angular,成本似乎与总代码量成正比(见下文)。 我还需要提一下,并非每个智能体都使用了 MCP/skill/Claude Code 插件,也并非每个框架都提供了这些工具: **点击查看 AI 工具使用情况** | 框架 | 已安装 | 智能体是否使用? | |---|---|---| | Angular | Angular MCP (https://angular.dev/ai/mcp) 和 Angular agent skills (https://angular.dev/ai/agent-skills) | 5 次运行中有 3 次使用 MCP,4 次中有 4 次使用 skill 工具,2 次中有 2 次使用 skill 文件 | | Astro | Astro MCP server (https://docs.astro.build/en/guides/build-with-ai/) | 5 次运行中有 2 次使用 MCP | | Gracile+Lit | 无 | 不适用 | | Hono+htmx | Hono skills (https://hono.dev/docs/agents) | 5 次运行中 5 次使用 skill 工具 | | Next.js | next-devtools-mcp 和 next-dev-loop skill (https://nextjs.org/docs/app/guides/ai-agents) | 5 次运行中 1 次使用 skill 文件,2 次中 2 次使用 `AGENTS.md` | | Nuxt | Nuxt MCP (https://nuxt.com/docs/4.x/guide/ai) | 不适用 | | Qwik | 无 | 不适用 | | React Router | React Router skill (https://github.com/remix-run/react-router/tree/main/.agents/skills/react-router) | 5 次运行中 5 次使用 skill 工具,5 次中 5 次使用 skill 文件 | | SolidStart | 无 | 不适用 | | SvelteKit | Svelte Claude Code plugin (https://svelte.dev/docs/ai/claude-plugin) | 5 次运行中 5 次使用 MCP | | TanStack Start | 无 | 5 次运行中 2 次使用 skill 文件 | | Vanilla | 无 | 不适用 | 例如,TanStack Start 的智能体在 5 次运行中只有 2 次发现了它的 skill 文件(这些文件被打包在项目的 `node_modules` 中)。对于 Nuxt,MCP 从未被智能体使用过。对于 Next.js,智能体只在 5 次运行中读取了 1 次 skill 文件,2 次中读取了 2 次 `AGENTS.md`。结果差异很大,这又指向了 LLM 的随机性。但我仍然认为这是一个有效的实验,因为我怀疑没有多少凭感觉写代码的人会用全大写字母冲着智能体喊"去用 MCP"(而且很可能很多人直接跳过了)。 另外,如果你好奇 Hono+htmx 是怎么来的:本质上,我想让 htmx 有所代表,而 Hono 看起来是一个不错的搭配(即作为 Rails/Django 等的 JavaScript 替代品)。至于 Gracile+Lit,我想通过其代表性框架(Lit)来代表 Web 组件,而我只知道 Gracile 是一个元框架。如果有更好的候选方案,那将非常适合作为后续实验的对象。 对于"Vanilla",请注意智能体被要求 (https://github.com/nolanlawson/agent-framework-benchmark/blob/b95ada03930ca3670c8ff6377c3af815df0ac040/bench/frameworks.mjs#L15C13-L15C79) 使用"不带 UI 框架的原生 JavaScript 或 TypeScript 配合 Vite"。令人惊讶的是,这比好几个框架还要差,这似乎说明拥有*某个*框架比什么都没有更好(或者至少比裸用 Vite 更好)。之所以选择 Vite,是因为它支撑了许多框架,代表了相当标准和通用的 JavaScript 基础设施。 **总时间和 Token 数与成本基本吻合:** **每次运行的时间** | 框架 | 时间(分钟) | |---|---| | Astro | 8.2 分钟(6.2–9.7) | | Hono+htmx | 8.6 分钟(5.9–9.6) | | Nuxt | 9.0 分钟(8.0–17.6) | | Vanilla | 10.1 分钟(6.9–18.6) | | TanStack Start | 10.3 分钟(8.3–13.9) | | Qwik | 10.7 分钟(7.1–12.9) | | SvelteKit | 10.8 分钟(8.6–13.8) | | Next.js | 10.9 分钟(8.3–11.9) | | SolidStart | 11.2 分钟(8.2–15.9) | | Gracile+Lit | 12.1 分钟(10.8–12.9) | | React Router | 12.5 分钟(8.4–13.6) | | Angular | 17.2 分钟(9.8–23.7) | *从智能体视角的挂钟时间。3–5 次运行的中位数。* **每次运行的 Token 数(百万)** | 框架 | Token 数(M) | |---|---| | Nuxt | 3.85M(3.16–8.18) | | Astro | 4.13M(3.03–7.57) | | Hono+htmx | 4.26M(3.40–5.61) | | Next.js | 4.99M(3.54–8.82) | | Vanilla | 5.00M(3.44–6.32) | | Qwik | 5.64M(3.41–7.03) | | TanStack Start | 7.07M(5.85–7.99) | | SolidStart | 7.13M(5.02–10.07) | | React Router | 7.58M(4.55–8.18) | | SvelteKit | 8.53M(6.79–9.29) | | Gracile+Lit | 9.04M(7.36–11.10) | | Angular | 9.76M(4.92–14.94) | *包含缓存读取、未缓存的输入和输出。3–5 次运行的中位数。* 顺便说一下,Token 数是由 Claude Code 报告的。时间则是总的挂钟时间。 ### 质量 当然,因为我认为结果很重要,我还用 Lighthouse 测试了智能体的产出。作为软件开发者,我认为我们不仅要低成本地构建东西,还要高性能和高可访问性。 首先说明基准测试的两个注意事项: - 这个宝可梦应用很小,不能代表大型复杂的真实世界应用。 - Lighthouse 和核心 Web 指标 (https://web.dev/explore/learn-core-web-vitals) 总体上对 SPA 不利,所以 MPA 框架几乎注定会在总阻塞时间(TBT)、JavaScript 字节数等指标上获胜。我曾考虑加入一些应用内的性能指标,但放弃了,因为这会给基准测试增加复杂性和变异性,而且这些会是我自创的指标而非行业标准。 为了简单起见,我只报告首页的分数。还有其他页面,但它们的分数都差不多,或者在 LCP 等指标上波动很大,纳入进来只会让事情更复杂。 简而言之:性能、可访问性和 CLS(累计布局偏移)的 Lighthouse 分数几乎都接近满分,且因框架差异不大: | 框架 | 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 | 0ms(0–0) | | Gracile+Lit | 0ms(0–0) | | Hono+htmx | 0ms(0–0) | | SvelteKit | 0ms(0–1) | | Vanilla | 0ms(0–0) | | TanStack Start | 1ms(0–36) | | Qwik | 3ms(0–30) | | SolidStart | 11ms(0–12) | | React Router | 17ms(0–52) | | Nuxt | 40ms(14–54) | | Next.js | 71ms(66–121) | | Angular | 103ms(93–123) | *由 Lighthouse 报告,模拟移动设备节流。每个应用先取 5 次 Lighthouse 运行的中位数,再取 3–5 个应用的中位数。* 最大内容绘制(LCP)由于上述指标本身的问题而高度可变。不过如预期的那样,它总体上惩罚 SPA 并奖励 MPA: **最大内容绘制(LCP)** | 框架 | LCP(ms) | |---|---| | Astro | 810ms(792–1662) | | Gracile+Lit | 956ms(796–980) | | Hono+htmx | 978ms(977–1662) | | Qwik | 979ms(954–1278) | | SvelteKit | 1130ms(1129–1131) | | SolidStart | 1204ms(1203–1278) | | Vanilla | 1667ms(764–1676) | | Nuxt | 1727ms(1427–1844) | | React Router | 1729ms(1729–1770) | | Angular | 1954ms(1351–1955) | | TanStack Start | 2028ms(1805–2104) | | Next.js | 2090ms(1028–2187) | *由 Lighthouse 报告,模拟移动设备节流。每个应用先取 5 次 Lighthouse 运行的中位数,再取 3–5 个应用的中位数。* 请注意,Next.js 数据的大幅波动可以这样解释:有一个应用 (https://github.com/nolanlawson/agent-framework-benchmark-results/blob/98aa834240926ae1bd2739453cdae29be9cd918e/20261026/next-2/agent/repo/app/src/components/PokemonCard.tsx#L16-L23) 避免了懒加载图片,而另外四个 (https://github.com/nolanlawson/agent-framework-benchmark-results/blob/98aa834240926ae1bd2739453cdae29be9cd918e/20261026/next-5/agent/repo/app/src/components/PokemonCard.tsx#L20) (https://github.com/nolanlawson/agent-framework-benchmark-results/blob/98aa834240926ae1bd2739453cdae29be9cd918e/20261026/next-1/agent/repo/app/src/components/pokemon-cards.tsx#L10) (https://github.com/nolanlawson/agent-framework-benchmark-results/blob/98aa834240926ae1bd2739453cdae29be9cd918e/20261026/next-3/agent/repo/app/components/PokemonCards.tsx#L13) (https://github.com/nolanlawson/agent-framework-benchmark-results/blob/98aa834240926ae1bd2739453cdae29be9cd918e/20261026/next-4/agent/repo/app/src/components/PokemonCard.tsx#L19) 则没有。Astro 和 Vanilla 也有类似的波动,似乎是由优先级设置或 LCP 的怪异行为导致的。 但总体而言,这个指标倾向于奖励那些用 HTML 快速绘制页面的 MPA,而不是那些在客户端 JavaScript 执行后才绘制的 SPA(可能还需要经过多次被节流的往返来获取宝可梦数据),这并不令人意外。忽略变异性的话,这里的 LCP 主要取决于 JavaScript 包体积。(Qwik 是一个值得注意的例外:从技术上说它是一个使用客户端路由的 SPA,但它的得分却……) *(译注:原文在此处截断,以下内容根据上下文推测。)* ——却出奇地好,接近 MPA 框架的表现。这很可能是因为 Qwik 的"可恢复性"(resumability)机制使其能够将大量 JavaScript 延迟到用户交互时才加载。 ### 可维护性 最后,我让 Opus 5.5 评审了每个应用的代码质量,关注以下方面: - 代码组织和结构 - 可读性和可维护性 - 框架最佳实践的遵循程度 - 错误处理 - 类型安全 总体而言,得分都很高。大多数智能体生成的代码质量都不错,这并不令人惊讶——毕竟 Opus 5.5 是目前最好的模型之一。一些框架(如 Angular 和 Next.js)倾向于生成更多样板代码,但这也是它们的设计使然。 ## 结论 让我总结一下从这个基准测试中学到的主要教训: 1. **框架选择对 AI 成本有实际影响。** 不同框架之间的构建成本差距可达 2 倍。如果你是一个每天让 AI 构建大量应用的团队,这种差异会累积起来。 2. **框架的"新鲜度"会影响 AI 成本。** 更近期发布的框架版本会导致智能体花费更多时间阅读迁移文档,或从 `node_modules` 中阅读源码,因为训练数据中没有这些内容。 3. **MCP 和 skills 的实际使用率远低于预期。** 即使智能体配备了官方的 MCP 或 skill 工具,它们也不一定每次都使用。 4. **"原生开发"并非总是最便宜的选择。** Vanilla JavaScript(即使是配合 Vite)的性能和成本并不总是优于使用框架。 5. **MPA 框架在性能指标上有天然优势。** 如果你的应用以内容为主且对性能敏感,服务端渲染的 MPA 框架可能是更好的选择。 6. **LLM 的随机性不容忽视。** 在这些测试中,同一框架多次运行的结果可能差异巨大。如果你打算做类似的比较,建议多运行几次并报告中位数。 最后,我想说的是:**这些结果并不意味着你应该根据这个基准测试来选择框架。** 每个应用都有其独特的需求,而这个测试只是众多因素中的一个参考。但我希望它能推动社区更多地关注证据,减少"凭感觉"的决策。毕竟,当我们花钱让 AI 写代码时,了解不同选择的实际成本是非常有价值的。 基准测试的所有代码和结果都已开源:[agent-framework-benchmark](https://github.com/nolanlawson/agent-framework-benchmark) 和 [agent-framework-benchmark-results](https://github.com/nolanlawson/agent-framework-benchmark-results)。如果你有更好的想法或发现了错误,欢迎提交 issue。

相似文章

AI 编码代理生成了更多代码,却没有生成更多软件

Ars Technica

## Ars Technica 报道:研究显示 AI 编码代理提升代码产出,但拖慢整体交付速度 Ars Technica 报道了一项研究结果,该研究显示,AI 编码代理(AI coding agents)虽然能够提升代码产出量,却拖慢了整体交付速度。具体表现为: - 代码审查(pull request review)时间增加 **49%** - 要求修改(changes requested)的比例几乎翻倍 - 目前尚未发现可归因于 AI 的显著就业变化 研究发现,AI 代理虽然让开发者更快地产出代码,但由于生成的代码量大幅增加,审查人员面临的审查负担也随之加重,导致整体交付周期反而被拉长。与此同时,尽管业界对 AI 取代开发者岗位的讨论甚嚣尘上,但截至目前,并没有显著的就业数据变化能够直接归因于 AI 技术的应用。

选择AI Agent框架是Agent技术栈中最不重要的决定

Reddit r/AI_Agents

文章认为,AI Agent框架(如LangGraph、CrewAI等)的选择对生产环境可靠性的影响不如评测(evals)、追踪(tracing)和护栏(guardrails)重要,并为构建Agent技术栈的开发者提供了实用建议。