原文:www.lorenstew.art/blog/10-kan...
原文结论先行:作者对团队的建议:基于此评估,Marko、SolidStart和SvelteKit都是我们移动优先需求的优秀选择。真正的问题是优先级:绝对最小的包(Marko)、最容易的React迁移(SolidStart)或最佳全方位开发者体验(SvelteKit)。如果团队有Vue经验,Nuxt也很有吸引力,具有其成熟生态系统和有竞争力的性能。
对于工作以外的个人项目,我将使用 SvelteKit 并越来越多地使用 Marko。它们的开发者体验感觉正好,代码自然流畅,它们使构建事物变得有趣。
译者感想:看完文章我也被 SvelteKit 吸引,最近刚好有新闻苹果 App Store 就是用 Svelte 构建,不过源码最后被 DMCA(数字千年版权法)限制无法共享(Repository unavailable due to DMCA takedown)。

我用10种框架开发了同款应用:移动端性能框架评估
2025年10月28日 | 修订于2025年11月2日
最初为工作评估3个框架,结果做了10个。新一代框架(Marko、SolidStart、SvelteKit、Qwik)都能实现35-39毫秒的即时性能。真正的区别在哪?压缩后包体积从28.8 kB到176.1 kB不等。根据你的优先级选择,而不是纠结于微小的FCP差异。
为什么做这个测试
我的团队需要为即将开发的应用选择一个框架。需求很明确:必须在移动设备上表现良好。我们正在为实地工作的房地产经纪人开发工具。他们需要在房地产开放日(Open houses)、停车场和信号不稳定的区域使用我们的应用。当有人站在潜在买家面前试图展现专业形象时,加载缓慢的应用会让他们显得很不专业。
译注:Open houses 这里指房地产的"开放日"或"看房日"。在这种情况下,信号不好的原因通常是:用户集中。在开放日活动中,大量潜在买家、房产经纪人和邻居可能会聚集在同一区域,所有人的手机都连接到同一个或附近的蜂窝网络塔。这会导致网络拥堵
我从一个看似合理的比较开始:Next.js(我们目前需要框架时的默认选择)与SolidStart和SvelteKit(我听说不错的替代方案)。我以为比较三个框架应该很简单。但当我构建了第一个实现并测量包体积时,我注意到了显著差异。Next.js的压缩包体积为154到176 kB,而SolidStart和SvelteKit的压缩包体积为30到54 kB。我想知道这些差异是特定于单个框架的实现,还是框架家族间的系统性差异?如果React、Angular和Vue都采用相似的架构方法,它们的包体积是否会遵循相似的模式?我决定扩展评估范围来找出答案。
这个问题改变了项目范围。如果我要为团队提供真正的建议,我需要测试所有主要的元框架,并了解替代方案的全貌。三个框架变成了十个。这个最初为工作做的实用评估变成了更大的事情:对2025年移动端网页性能可能性的半全面审视。
如果你对框架多样性为何重要的理论意义感兴趣,我在React默认胜出中写过相关文章。本文重点关注数据。
本文分享我在十个框架中编写看板应用后的发现。极端情况是Marko,其首页原始包体积为12.4 kB(压缩后6.8 kB),而Next.js首页原始包体积为486.1 kB(压缩后150.9 kB)。这是首页原始大小的39倍差异。对于看板页面,Marko提供88.8 kB原始大小(压缩后28.8 kB),而Next.js为563.7 kB原始大小(压缩后176.1 kB),压缩大小差异达6.11倍。这些差异在蜂窝网络上转化为真实的秒级延迟。
代码可在此处找到。
译注:本文缺少代码量比较。使用基于 Rust 的代码量统计工具 tokei。
统计目标共十个:
scss
kanban-comparison
├── kanban-analog
├── kanban-htmx
├── kanban-marko
├── kanban-nextjs
├── kanban-nuxt
├── kanban-qwikcity
├── kanban-solidstart
├── kanban-sveltekit
├── kanban-tanstack
└── kanban-tanstack-solid
统计命令:
sh
find . -maxdepth 1 -type d -name "*kanban*" -exec sh -c 'echo && echo "📊【$(basename "$1")】"; tokei "$1" --exclude "*.md" --exclude "*.json"' _ {} \;
统计结果:
markdown
📊【kanban-analog】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 11 9 1 1
HTML 1 15 15 0 0
SQL 2 200 160 29 11
SVG 5 5 5 0 0
TypeScript 45 3272 2908 65 299
===============================================================================
Total 54 3503 3097 95 311
===============================================================================
📊【kanban-htmx】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 24 17 3 4
JavaScript 5 3498 3474 13 11
SQL 1 68 62 6 0
SVG 1 9 9 0 0
TypeScript 21 1821 1542 67 212
===============================================================================
Total 29 5420 5104 89 227
===============================================================================
📊【kanban-marko】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 4 3 0 1
JavaScript 6 268 222 13 33
SQL 1 68 62 6 0
SVG 1 41 41 0 0
TOML 1 9 8 0 1
TypeScript 26 1658 1365 90 203
===============================================================================
Total 36 2048 1701 109 238
===============================================================================
📊【kanban-nextjs】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 3 3 0 0
JavaScript 3 98 86 2 10
SQL 2 164 132 20 12
SVG 5 5 5 0 0
TSX 27 1927 1735 27 165
TypeScript 21 1473 1210 80 183
===============================================================================
Total 59 3670 3171 129 370
===============================================================================
📊【kanban-nuxt】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 3 2 0 1
JavaScript 2 45 39 2 4
SQL 1 68 62 6 0
TypeScript 18 1282 1069 30 183
-------------------------------------------------------------------------------
Vue 15 77 63 0 14
|- HTML 15 819 763 13 43
|- JavaScript 12 675 554 26 95
(Total) 1571 1380 39 152
===============================================================================
Total 37 1475 1235 38 202
===============================================================================
📊【kanban-qwikcity】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 79 60 6 13
JavaScript 4 116 108 2 6
Shell 1 19 12 3 4
SQL 2 165 132 21 12
SVG 1 1 1 0 0
TSX 21 2578 2213 101 264
TypeScript 16 1133 937 75 121
===============================================================================
Total 47 4091 3463 208 420
===============================================================================
📊【kanban-solidstart】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 4 2 0 2
JavaScript 3 52 43 2 7
Shell 1 18 13 1 4
SQL 1 69 62 7 0
TSX 20 2022 1820 44 158
TypeScript 19 1351 1079 97 175
===============================================================================
Total 45 3516 3019 151 346
===============================================================================
📊【kanban-sveltekit】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 20 15 1 4
HTML 1 11 11 0 0
JavaScript 2 49 40 2 7
Shell 1 52 39 4 9
SQL 1 68 62 6 0
SVG 1 1 1 0 0
Plain Text 1 3 0 3 0
TypeScript 18 1761 1567 46 148
-------------------------------------------------------------------------------
Svelte 14 890 831 3 56
|- CSS 2 30 26 0 4
|- JavaScript 13 324 267 12 45
(Total) 1244 1124 15 105
===============================================================================
Total 40 2855 2566 65 224
===============================================================================
📊【kanban-tanstack】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 9 8 0 1
JavaScript 2 113 90 7 16
SQL 1 68 62 6 0
TSX 29 2348 2083 47 218
TypeScript 14 1106 905 69 132
===============================================================================
Total 47 3644 3148 129 367
===============================================================================
📊【kanban-tanstack-solid】
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 9 8 0 1
JavaScript 4 278 225 18 35
SQL 1 68 62 6 0
TOML 1 11 8 2 1
TSX 29 2374 2110 54 210
TypeScript 18 1404 1141 97 166
YAML 1 3 3 0 0
===============================================================================
Total 55 4147 3557 177 413
===============================================================================
统计结论:
1. 代码量排名(按总行数,排除 markdown 和 JSON):
- kanban-htmx (5,104) - 最庞大
- kanban-tanstack-solid (3,557)
- kanban-qwikcity (3,463)
- kanban-nextjs (3,171)
- kanstack-tanstack (3,148)
- kanban-analog (3,097)
- kanban-solidstart (3,019)
- kanban-sveltekit (2,566)
- kanban-marko (1,701)
- kanban-nuxt (1,235) - 最精简
前三
- 🥇 kanban-nuxt
- 🥈 kanban-marko
- 🥉 kanban-sveltekit
2. 架构复杂度
文件数量:
- kanban-nextjs (59) - 最复杂
- kanban-analog (54)
- kanban-tanstack-solid (55)
- kanban-sveltekit (40) - 相对简洁
- kanban-nuxt (37) - 最简洁
前三:
- 🥇 kanban-nuxt
- 🥈 kanban-sveltekit
- 🥉 kanban-tanstack-solid
统计结论:
简洁性和架构复杂性:SvelteKit 和 Nuxt 在代码量和文件数量上有明显优势。
移动端选择个人推荐 SvelteKit。在移动端场景下,其编译时优化和极简的包体积优势太明显了!
为什么移动端网页性能很重要
对于本次评估,移动性能是主要约束。我们的用户是实地工作的房地产经纪人。他们需要我们的技术在30人同时使用同一蜂窝塔的开放日、看房间隙的停车场以及其他没有WiFi办公桌的地方都能良好工作。我们的经纪人需要快速工作的工具,而不是"最终能加载"的工具。
我所在的公司没有资源构建原生应用。我们为网页开发,这意味着如果它有URL,人们就会在手机上访问它。对我们的用户来说,应用在手机上的使用频率可能与桌面端一样高。
这一现实塑造了评估标准。我不能只选择一个"能在移动端工作"的框架。我需要的是在蜂窝网络连接下真正表现良好的方案。框架提供30 kB与170 kB之间的差异不是理论上的。它关系到应用是让人感觉专业,还是让我们的用户在客户面前显得糟糕。
性能缓慢的业务成本 :SpeedCurve的Tammy Everts的研究揭示了一些令人惊讶的发现。虽然网站停机 会导致9%的永久用户流失,但性能缓慢会导致28%的永久流失。这要糟糕3倍多。事实上,减速发生的频率是中断的10倍,尽管每小时成本较低,但总收入影响大约为2倍。除了流失数字,性能缓慢还会产生心理效应,用户开始对你的整个品牌产生负面看法。内容显得"无聊",设计看起来"俗气",即使这些元素没有改变。缓慢会毒害一切。
现实世界的成本:在3G速度(1.6 Mbps,150ms RTT)下,147 kB的差异大约相当于额外1秒的下载时间,加上移动CPU解析和执行所需的500毫秒到1秒。总计:框架间慢1.5到2秒。即使3G网络逐步淘汰,我们为之构建的房地产经纪人经常经历相当于3G的速度。拥挤的开放日、看房间隙的停车场和低覆盖区域的物业(这因蜂窝提供商而异)都会造成不稳定的4G条件,其中10 Mbps降至1到2 Mbps。147 kB的包差异在拥塞的4G上仍然会转化为1秒以上的延迟。
"但它被缓存了!" 这种反对意见忽略了现实。缓存破坏是标准做法。每次部署都意味着用户需要重新下载。第一印象很重要。第二、第三和第十次印象也很重要。你的用户记住负面体验远多于正面体验。
这就是为什么我将评估范围扩展到最初的三个框架之外。我需要看到完整的选项范围。当有人在看房间隙的停车场打开应用时,每一秒都很重要。优先为移动性能构建意味着桌面端在WiFi上默认表现优异。反之则不然。为桌面端优化而移动用户受苦。
我发现框架之间的差异反映了根本不同的工程优先级。一些框架优先考虑运行时灵活性,提供广泛的抽象以支持广泛的用例。其他框架则从根本上优先考虑运行时大小和移动性能。我测量的看板页 bundle 大小差异高达6.1倍(从压缩后28.8 kB到176.1 kB),这些差异在蜂窝网络上非常重要。
关键要点(TL;DR)
所有测试框架都实现了即时FCP: 现代框架在35-71毫秒范围内实现了出色的首次内容绘制性能,使初始页面加载在所有框架中都感觉即时。真正的区别在于包大小:框架范围从28.8 kB(Marko)到176.1 kB(Next.js)压缩,差异达6.1倍,通过增加数据使用量、更长的解析时间和每次访问时更大的电池消耗影响移动用户。
包大小冠军:Marko 为看板页面提供88.8 kB原始大小(压缩后28.8 kB),比Next.js的563.7 kB原始大小(压缩后176.1 kB)小6.11倍。这比下一个最接近的竞争对手(SolidStart为41.5 kB压缩)小31%,使Marko成为包大小是绝对最高优先级时的明确选择。
可恢复性模式:Marko和Qwik City 都通过可恢复性消除了传统的水合作用。Marko通过构建时分析实现最小的包(压缩后28.8 kB),而Qwik(压缩后58.4 kB)使用延迟可恢复性与渐进式加载。两者都无需在客户端重新执行组件即可实现即时交互性。
Nuxt证明成熟框架可以竞争: 在224.9 kB原始大小(压缩后74.7 kB)和38毫秒FCP下,Nuxt证明了成熟的"三大"框架在适当配置时可以实现新一代性能。Vue的架构允许在维护成熟生态系统的同时实现有竞争力的移动网页性能。React和Angular没有显示达到类似结果的路径。
关键扩展差异:MPA框架(Marko、HTMX)每个页面提供最少的JavaScript,随着你添加功能保持精简。SPA框架预先提供路由和框架运行时,即使使用代码分割也有较高的基线。Marko无论总路由数多少,都提供大约12.4到88.8 kB的原始大小。SPA维持83.9到563.7 kB的原始大小基线加上路由块。
主导框架显示出显著不同的包大小。TanStack Start (React) 使用React 19实现373.6 kB原始大小(压缩后118.2 kB)的包,仅比Next.js的563.7 kB原始大小(压缩后176.1 kB)好1.49倍。Angular 通过Analog实现了有竞争力的包,为376.3 kB原始大小(压缩后103.9 kB),将其定位与TanStack Start相似并领先于Next.js。Vue(通过Nuxt)证明更好,实现了224.9 kB原始大小(压缩后74.7 kB)的包,与新一代框架相匹配。
同时,新一代框架如SolidStart 提供128.6 kB原始大小(压缩后41.5 kB)的包,比Next.js小4.24倍,比使用React的TanStack Start小2.85倍。完美的对照比较:使用React的TanStack Start(373.6 kB原始大小)与使用Solid的TanStack Start(182.6 kB原始大小)。相同的元框架,相同的模式,但React包的大小是Solid的2倍,隔离了React的运行时成本。
移动端就是网页。 这些测量很重要,因为移动网页是数十亿人的主要互联网。如果你的应用可以通过URL访问,人们将在具有蜂窝连接的手机上使用它。为桌面端优化并希望移动端足够好是本末倒置。网页是移动的。为这一现实构建。
每个构建使用相同的数据库、功能和UI,因此比较保持公平。看板页面的包大小差异与现代替代方案相比,比Next.js小4.24到6.11倍。重要提示:这些测量代表了具有最少依赖项的严格基线实现。 真实的生产应用通常由于分析、身份验证、功能标志和第三方库而多提供5到10倍的JavaScript,这意味着框架差异在实践中显著复合。在具有蜂窝连接的移动设备上,这非常重要。
包大小现实检查
React(通过Next.js)提供150.9到176.1 kB压缩后(486.1到563.7 kB原始大小)。Angular(通过Analog)为两个页面提供103.9 kB压缩后(376.3 kB原始大小),实现了有竞争力的包大小,将其定位与TanStack Start相似并领先于Next.js,尽管仍然比新一代框架重。Vue(通过Nuxt)与两者都不同。Nuxt提供更具竞争力的包大小,为74.7 kB压缩后(224.9 kB原始大小),使其成为唯一在移动网页包大小上竞争的"三大"元框架。React需要架构更改才能实现类似结果。Angular(通过Analog)已显示改进,但仍然比新一代替代方案重。Nuxt证明,通过适当的优化,即使成熟的框架也可以提供有竞争力的包大小。
React的明确策略: 移动端用React Native。使React在网页上变重的架构选择是故意的,并解决了桌面开发的真实问题。但对于移动网页,React的立场是:改用React Native。权衡是React Native将开发人员推入应用商店,平台持有者在那里提取高达30%的交易并控制分发。
其他框架的设计使得,通过规范,开发人员可以创建出色的移动体验。Marko、Solid、Svelte、Qwik和Vue背后的团队构建了优化网页作为移动端一等公民平台的解决方案。
看板页面包大小(从小到大)
| 框架 | 看板页面原始大小(压缩后) | 首页原始大小(压缩后) | 与Next.js的差异(看板页面) |
|---|---|---|---|
| 🥇 Marko | 88.8 kB (28.8 kB) | 12.4 kB (6.8 kB) | 小6.11倍 |
| 🥈 Qwik City | 114.8 kB (58.4 kB) | 86.5 kB (42.5 kB) | 小3.02倍 |
| 🥉 SvelteKit | 125.2 kB (54.1 kB) | 103.4 kB (47.8 kB) | 小3.26倍 |
| 🏅 Astro + HTMX | 127.3 kB (34.3 kB) | 86.9 kB (21.5 kB) | 小5.13倍 |
| 🏅 SolidStart | 128.6 kB (41.5 kB) | 83.9 kB (29.8 kB) | 小4.24倍 |
| TanStack Start + Solid | 182.6 kB (60.4 kB) | 149.4 kB (50.8 kB) | 小2.92倍 |
| Nuxt | 224.9 kB (74.7 kB) | 224.9 kB (74.7 kB) | 小2.36倍 |
| TanStack Start | 373.6 kB (118.2 kB) | 309.4 kB (98.3 kB) | 小1.49倍 |
| Analog | 376.3 kB (103.9 kB) | 376.3 kB (103.9 kB) | 小1.70倍 |
| Next.js 16 | 563.7 kB (176.1 kB) | 486.1 kB (150.9 kB) | 基线 |

性能背景 :所有测试的框架都实现了出色的Lighthouse分数(100),具有相似的首次内容绘制时间。由于性能基本相同,包大小是这些框架为移动用户区分的关键。6.1倍的范围对于数据使用量、解析时间和电池消耗很重要。
现场数据验证 :Chrome用户体验报告(CrUX)提供了来自数百万实际网站在移动设备上使用这些框架的真实世界核心网页指标数据。这些现场数据补充了本文中的受控测量。重要说明:CrUX数据反映了这些框架由普通开发者在生产中的使用方式,而不是最优实现。如果一个框架在CrUX中表现不佳但在这些测试中表现良好,它证明了通过适当配置、性能调优和依赖规范可以实现的目标。现场数据与优化实现之间的差距揭示了实际使用模式中的改进机会。
Marko的88.8 kB原始大小(压缩后28.8 kB)与Next.js的563.7 kB原始大小(压缩后176.1 kB)之间的差异大约相当于蜂窝网络上的1.5秒。这些秒数是基线。随着每个功能和每个依赖项的添加,等待加载的时间会增加。
关于HTMX的重要背景:Astro + HTMX通过用服务器驱动的交互换取客户端响应性,以最简单的代码库实现了其小体积。对于需要丰富客户端状态的应用,Marko、Solid和Svelte在保持精简的同时提供完整的响应性。
在深入之前,提醒一下我的渐进式复杂性宣言:这里比较的框架代表了第5级复杂性。当你需要统一的客户端状态、大量响应性和/或客户端导航时,它们是强大的工具。但大多数应用在较低级别上蓬勃发展。例如,第3级(使用HTMX和原生JavaScript增强的服务器渲染HTML,如本仓库中的kanban-htmx应用所示)可以用最少的JavaScript处理复杂的交互式应用。第4级使用Lit添加偶尔的Web组件以实现可重用元素。这些更简单的方法通常提供更小的包和更简单的代码库。本文重点关注需要第5级选项的情况,同时记住更简单的路径通常就足够了。
实验设置
我构建了一个看板应用十次,每次使用这些框架之一:Next.js 16(React 19,带有内置编译器)代表React的虚拟DOM方法,具有自动优化;TanStack Start(也是React 19)用于更精简的React元框架,没有App Router开销;TanStack Start + Solid(SolidJS 1.9)使用相同的元框架,具有细粒度响应性;Nuxt 4(Vue 3)用于Vue的响应式引用,具有SSR优先的开发者体验;Analog(Angular 20)使用Angular的现代信号API与元框架工具;Marko(@marko/run)用于流式SSR与细粒度响应性;SolidStart(SolidJS 1.9)用于原生Solid集成,通过信号实现细粒度响应性;SvelteKit(Svelte 5)用于通过符文实现细粒度响应性;Qwik City用于可恢复性而非水合作用;以及 Astro + HTMX 用于传统的MPA方法。
每个实现都包含完全相同的功能:看板创建和列表页面,每个看板四个固定列表(待办、进行中、质检、完成),卡片的完整CRUD操作,列表内和列表间的拖放卡片重新排序,从静态用户列表分配负责人,标签管理,带有作者跟踪的卡片评论,完成状态切换,拖放和图表更改的乐观UI更新(尽管HTMX缺少此功能),以及使用Valibot的服务器端表单验证。
所有十个应用共享相同的基础。数据库是SQLite,使用Drizzle ORM,在所有实现中使用相同的模式。样式来自Tailwind CSS加上DaisyUI以保持UI一致。每个框架实现包含大约17个组件。最重要的是,每个应用都对关系数据(看板→列表→卡片→标签/评论/用户)执行真实的数据库查询,而不是处理硬编码数组。
你可以在此处查看代码here。
关于依赖项的关键选择:这些应用有意最小化依赖项,与许多开发人员通常使用的情况相比。对于移动网页应用,每个依赖项都代表向用户提供额外千字节的选择。我使用了必要的UI库,如拖放包(因生态系统而异),但故意避免了数据获取库、状态管理助手和其他框架本机已经处理的实用程序。每个生态系统都有流行的包,它们增加了便利性但增加了包大小(React开发人员经常使用tanstack-query进行数据获取、状态管理库或表单助手)。为了说明权衡:仅tanstack-query就重约13 kB gzipped。这个单一的依赖项已经比Marko的整个首页包6.8 kB大。通过避免这些"锦上添花"的依赖项并使用每个框架的内置功能,你将看到的包差异反映了框架架构选择,而不是不同数量的功能或第三方助手。
测量方法 :所有包大小代表使用Chrome Lighthouse测量的生产构建产物。我报告原始(未压缩)JavaScript大小和压缩传输大小。原始大小反映了每个框架生成的实际代码量,并且对于比较更一致,因为它不因服务器压缩设置而异。压缩大小显示了用户实际通过网络下载的内容。所有实现使用相同的功能、数据库和UI框架以确保公平比较。有关详细信息,请参阅完整的测量方法。
React的实际天花板(TanStack vs Next)
TanStack Start实现98.3到118.2 kB压缩包(309.4到373.6 kB原始大小),而Next.js提供150.9到176.1 kB压缩(486.1到563.7 kB原始大小)。 两者都使用React 19。这仅是33%到35%的改进,主要反映了App Router + RSC开销。
Next.js提供完整的React服务器组件运行时加上序列化层、组件边界管理、缓存基础设施、具有所有路由功能的App Router、服务器操作的渐进增强、图像优化和中间件。TanStack Start去掉了大部分这些:没有RSC的传统SSR、更精简的路由和简单的RPC风格服务器函数。
两者都使用服务器端渲染,但Next.js的RSC模型增加了大量开销。服务器组件仅在服务器上渲染,客户端组件用"use client"指令标记,服务器将所有内容序列化为特殊格式,客户端需要运行时代码来反序列化和协调这些边界。TanStack Start使用更简单的传统SSR方法:在服务器上渲染,提供HTML,在客户端水合所有内容。没有序列化,没有边界协调。
在此测量中,Next.js的App Router + RSC增加了大约53到58 kB压缩。剩余的98.3到118.2 kB压缩(309.4到373.6 kB原始大小)是React的核心运行时成本,与协调、事件系统和水合相关。
使用Solid的 TanStack Start 证明了这一点: 使用相同的元框架与Solid而不是React提供182.6 kB原始大小(压缩后60.4 kB),比使用React的TanStack Start小49%。由于TanStack Router具有更多功能,这比SolidStart的128.6 kB原始大小(压缩后41.5 kB)大42%,但仍然远好于任何React选项。
!NOTE\] 译注:这段说明如果你需要使用 React,即 React 包体积没法避免的情况下,选择 TanStack Start 优于 Next.js。当然如果未来 TanStack 增加了更多功能,结论可能会有改变。
将React的基线与其他框架进行比较。Marko 提供6.8到28.8 kB压缩(12.4到88.8 kB原始大小),比使用React的TanStack Start小4.1倍 。SolidStart 使用JSX提供29.8到41.5 kB压缩(83.9到128.6 kB原始大小),小2.85倍到3.30倍。SvelteKit 实现47.8到54.1 kB压缩(103.4到125.2 kB原始大小),小2.06倍到2.18倍。Qwik 提供42.5到58.4 kB压缩(86.5到114.8 kB原始大小),小2.02倍到2.31倍。
React的架构(不仅是虚拟DOM,还有合成事件、平台修补和纯粹的功能复杂性)创造了无法避免的成本,没有元框架优化可以消除。 虚拟DOM实现可以很小(参见Preact为4 kB)。React的大小反映了规避平台约束和提供广泛功能的故意选择。要突破这个天花板并实现3到4倍小的包,你需要一个根本不同的架构方法。倾向于平台而不是规避它的框架可以提供显著的大小减少。React 团队选择接受这些成本以解决其他问题(服务器组件、统一模式)。这是一个合理的选择。但在React内部这是不可协商的。
回应常见批评
我知道你们中的一些人在想什么。"这样把 MPA 和 SPA 混在一起比较,真的公平吗?"以及"这个应用足够复杂吗?"
应用复杂性辩护:这不是某个玩具待办事项列表。这是一个坚实的中等复杂性应用,具有使用SQLite加上Drizzle ORM的真实数据库持久性、跨看板到列表到卡片到标签和评论的关系查询、拖放重新排序、(一些)乐观更新、模态框和服务器验证。它匹配团队每天快速构建的内部工具或MVP类型。包差异直接来自框架开销,而不是功能本身,并且这些差距在规模上随着更多路由和依赖项而变得更大。如果你的生产应用加入身份验证或实时功能,框架基线只会膨胀更多,而不是缩小。
MPA与SPA的细微差别 :路由模式辩论没有抓住重点,响应性模型更重要。借助视图转换API和推测规则API等功能,像Marko或HTMX这样的MPA在导航上感觉与SPA一样敏捷。真正的分裂在于扩展。MPA每个页面提供最少的JS,例如Marko保持在6.8到28.8 kB,而SPA携带83.9到563.7 kB的预先运行时基线加上块。
关于生态系统的说明 :"小生态系统"的担忧通常被夸大。对于移动优先的应用,我们应该对我们添加的每个依赖项都非常有选择性。每个包都会增加包大小和维护负担。像Claude、ChatGPT和Cursor这样的现代AI工具擅长为你的特定用例生成专注的代码。与其为一个50 kB的库导入3个功能,AI可以帮助你用2 kB编写你需要的代码。这种方法产生更小的包、你实际理解的代码和更少的供应链风险。大型生态系统有时是有利的,但当每个导入都花费你的移动用户时,它们也是一种负担。
结论:我的建议
完成十个实现并收集所有测量数据后,数据为我们移动优先的需求提供了明确的方向:
所有框架都提供即时初始加载。 有意义的差异是包大小。包在极端情况下有6.1倍的差距(压缩后28.8 kB到176.1 kB),通过数据成本、解析时间和电池消耗影响移动用户。根据包大小优先级和开发者体验进行选择。
也就是说,背景很重要。并非每个项目都可以或应该切换框架。
Next.js 仍然有意义的情况: 对于大型现有React代码库,迁移成本可能超过性能收益。如果你被困在React中无法迁移,考虑使用TanStack Start而不是Next.js,以获得33-35%的包减少,而没有App Router复杂性。然而,对于新项目,没有遗留问题需要维护,没有迁移成本需要权衡。选择构建在每次访问使用户多花费2倍到3倍JavaScript的基础上,意味着在更好选项不额外花费的情况下自愿接受更差的性能。"我们只懂React"不是技术约束,而是学习投资决策。在一个公司里面"组织政治(Organizational Politics)"因素确实存在,并且常常是最终决策的关键。但这并不能作为一个技术上的正当理由。这导致更好的选项存在但无法选择。
!TIP\] "**Organizational Politics**" 大概类似"政治正确",从纯技术和性能角度看,有比 React/Next.js 更好的选择。但为什么很多团队仍然坚持使用后者呢?"Organizational Politics" 就是那个非技术的、现实中的关键原因。 它指的是一个组织内部那些**非正式的、基于人际关系的、关乎权力、影响力和利益分配的决策过程**,而不是基于客观数据、逻辑或公司最佳利益的理性过程。
对常见反对意见的现实检查:
"但是招聘!" 有能力的开发人员学习框架。这些替代方案实际上比React更容易学习:没有钩子规则,没有依赖数组,没有手动记忆化舞蹈(manual memoization dance)。真正的困难不是学习曲线,而是创建一个承认约束并在这些约束下做出有意决策的工程文化。
"但是生态系统!" React的生态系统既是优势也是负担。大型库为你可能永远不会遇到的情况提供代码。那个带有全世界所有区域设置的日期选择器?你只需3个功能,但是你发布的代码里面包含300个。对于每个千字节都很重要的移动优先项目,这成为一个问题。现代AI工具使构建你需要的代码变得可行:自实现函数而不是为3个功能导入50kB的依赖。更小的包,造就更容易理解的代码。
"但这有风险!" 向蜂窝网络上的移动用户提供3倍大的包才是真正的风险。缓慢加载损害你的品牌并损失转化率。"安全选择"有可衡量的成本。
"但我的用户仅限于桌面端!" 老实说,"仅限于桌面端"通常是跳过性能规范的借口。而且这很少长期成立。六个月后有人问"我可以在手机上查看这个吗?",突然你被卡住了。最好从一开始就正确构建。桌面用户仍然受益于更快的解析和执行。即使在WiFi上,29.8 kB压缩加载明显快于176.1 kB压缩。更重要的是,当更好的选项不额外花费时,你为什么自愿接受3倍更差的性能?无论屏幕大小如何,性能都是一个特性。在约束下构建使你成为更好的工程师。"仅限于桌面端"不应该是"没有规范"。
为什么你应该认真考虑替代方案:使用这些新一代框架,性能是默认的。它们有2倍到6倍小的包,并且需要更少的优化工作。你将编写更少的代码,提供更少的JavaScript,并调试更少的框架怪癖。最重要的是,新项目应该根据优点而不是默认值做出选择。
这些替代方案特别有吸引力对于包大小直接影响用户体验的移动优先应用。在蜂窝连接上工作的移动专业人士,如房地产经纪人、现场服务人员、医护人员、送货司机和销售代表,受益最多。构建一个高性能网页应用而不是单独的网页、iOS和Android代码库意味着更小的团队、显著更低的开发成本和更快的迭代周期。
在替代方案中选择(按主要用例组织):
最小包:选择Marko 用于绝对最佳的包大小(压缩后6.8到28.8 kB)。当包大小是你的最高优先级时,Marko是明确的赢家。MPA架构每个页面提供最少的JavaScript,随着你添加路由保持精简。一旦你接受其流式模型,开发者体验非常出色。如果你喜欢html优先的方法,Marko这里是唯一选项(尽管SvelteKit接近)。注意:Marko 6目前处于测试版(在npm上标记为next),预计年底前退出测试版,没有预期的API更改,但正在进行错误修复和优化。
JSX熟悉度:选择SolidStart 如果你想要从React最容易的迁移路径。SolidStart使用JSX语法,具有自动依赖跟踪,消除了手动记忆化,同时让React开发人员立即感到熟悉。心智模型实际上比React更简单,因为信号比钩子更直接。
最佳全方位开发者体验:选择SvelteKit 用于平易近人的语法和出色的默认值。在125.2 kB原始大小(压缩后54.1 kB)下,SvelteKit提供比Next.js小3.26倍的包,默认具有渐进增强和最少的框架开销。基于编译器的方法意味着更少的运行时代码和更清晰的组件逻辑。凭借其对纯JS、CSS和HTML编写的关注,SvelteKit最适合来自任何背景的开发者,寻求具有少量框架怪癖的可读代码。
可恢复性模式:选择Marko或Qwik City 如果你想消除传统的水合开销。两者都避免在客户端重新执行组件,但方法不同。Marko (压缩后6.8到28.8 kB)通过构建时分析实现最小的包,为事件和效果捆绑 exactly 需要的代码,没有延迟加载。Qwik City(压缩后42.5到58.4 kB)使用延迟可恢复性,根据实际交互逐步加载JavaScript。选择Marko用于绝对最小的包,选择Qwik用于具有显著客户端功能且受益于渐进加载的应用。
成熟生态系统:选择Nuxt 如果你想要Vue的成熟生态系统与有竞争力的移动网页性能。在224.9 kB原始大小(压缩后74.7 kB)下,Nuxt证明了成熟的"三大"框架在适当配置时可以实现新一代性能。最适合已经熟悉Vue的团队、受益于广泛社区包的项目或重视成熟框架安全性的团队。Nuxt在熟悉与性能之间架起了桥梁。
当开发人员有真正的替代方案时,每个人都是赢家。如果SolidStart和Svelte没有证明自动优化很重要,React就不会添加编译器。当我们停止接受"足够好"作为天花板时,整个生态系统都会改进。
我对团队的建议: 基于此评估,Marko、SolidStart和SvelteKit都是我们移动优先需求的优秀选择。真正的问题是优先级:绝对最小的包(Marko)、最容易的React迁移(SolidStart)或最佳全方位开发者体验(SvelteKit)。如果团队有Vue经验,Nuxt也很有吸引力,具有其成熟生态系统和有竞争力的性能。
对于工作以外的个人项目,我将使用SvelteKit并越来越多地使用Marko。它们的开发者体验感觉正好,代码自然流畅,它们使构建事物变得有趣。
开发者体验:更简单的心智模型
除了包大小,这些替代方案通常具有更简单的心智模型。React的钩子需要手动依赖数组,当错误时会导致难以调试的过时闭包。Solid、Svelte和Marko使用自动依赖跟踪。Vue需要.value访问以实现响应性。Angular带来依赖注入复杂性。更简单的心智模型与更小的包相关,因为更少的运行时复杂性意味着更少的代码需要提供。
Web 是最后的公地(the Last Commons):为何此事超越框架之争
TIPS\] 译注:这部分主要是从数字文明层面表述"构建网页应用很重要",网络是最后一块净土,从框架纷争到文明存续后半段升华到数字文明层面,网页是突破技术封建主义的重要手段。
当你向App Store或Google Play提供原生应用而不是构建网页应用时,你不仅仅是在做技术决策。你正在接受一项二十年前无法想象的交易。苹果和谷歌各自抽取每笔交易的30%(根据程序和类别有例外)。他们制定规则。他们决定你可以提供什么。他们可以明天撤销你的访问权限,没有追索权。你没有替代市场。你甚至无法在价格上竞争,因为费用已计入许多交易。
经济学家Yanis Varoufakis在他的同名书中称此为"技术封建主义"。App Store不是市场,它是封地。开发人员是数字农奴,绑定在云领主的土地(他们的平台)上,没有退出。用户也被锁定在其中。App Store是一个精心策划的花园,由两家公司拥有的算法决定你看到什么。你的数据被收集。你的选择被过滤。你不是有替代方案的客户,你是围墙花园中的臣民。
网页是不同的。没有单一公司抽成,没有算法策划你的选择,分发是直接的。用户实际上可以用脚投票。它不完美,但它是我们拥有的最接近开放市场的东西,开发人员保留代理权,用户保留选择权。
当公司放弃网页转向仅限应用时,他们不是在做出中立的技术决策。他们自愿将用户从竞争市场转移到封建系统中。是的,我知道这听起来很戏剧化,但Varoufakis花了多年时间记录数字平台的经济学如何创造了这种动态。
为什么你应该关心,无论你相信什么:
如果你倾向于资本主义,应用商店创造的环境与资本主义应有的相反。垄断租金提取取代了竞争和创新。没有市场机制挑战它们。那不是资本主义,那只是提取。
如果你倾向于反资本主义,技术封建主义 arguably 比常规资本主义更糟,因为至少资本主义有摩擦和监管手段。这个两者都没有。这是零市场竞争的完全私人控制。
无论哪种方式,网页是经济活动可以在科技寡头拇指之外发生的最后一个地方。构建网页应用很重要。提供更小、更快、高性能的网页应用更重要,而大多数网页流量来自移动网页。你节省的每个千字节都是团队选择网页而不是构建受应用商店控制和费用约束的原生应用的另一个理由。
结论:此评估揭示的内容
最初为即将到来的工作项目做的简单框架比较变成了更揭示性的事情。数据清楚地显示了当框架从一开始就优先考虑移动网页性能时可能实现的目标。
评估揭示了当你重新思考基础时会发生什么。SolidStart、SvelteKit、Qwik和Marko代表了不同的架构优先级,以主导框架无法的方式推动边界。竞争驱动创新。这些替代方案显示了当移动网页性能是主要设计约束而不是事后考虑时可以实现的目标。
对于服务于蜂窝网络上的移动专业人士的团队(如我们的团队),这些成本在每次访问时支付。React和Angular经常面临架构性能天花板。Vue证明了成熟的框架在适当配置时可以竞争。记住,这些测量代表初始页面加载。MPA框架在所有路由上保持其精简配置文件,而SPA将路由块添加到其基线。
对于任何开始新项目的人,评估提出了一个重要问题:我们有何必要,让应用凭空增加 6.1 倍的体积? (这还只是在添加通常使生产包大小增加5到10倍的身份验证、分析和第三方库之前,现实世界差距更大)。如果你在开始时做出良好的架构决策,构建一个在移动端运行良好的应用并不困难。早期的正确选择意味着你的应用在所有地方都表现良好,而不仅仅是在桌面WiFi上。选择MPA框架用于最精简的每页包,或轻量级SPA用于具有熟悉模式的出色性能。
如果你想构建比竞争对手更好的产品,为什么你会像他们一样构建? 当每个人都使用Next.js时,在性能上获胜意味着逆流对抗React的设计。当你改用Marko、SolidStart、SvelteKit或Nuxt时,优势轻松而来。你的应用默认更快。你的包更小,无需优化工作。你的用户获得更好的体验,无需额外努力,给你真正的差异化。
当你选择Marko并提供28.8 kB而不是Next.js的176.1 kB时,你的用户在蜂窝网络上获得更好的体验。网页变得更具竞争力,对公司来说是一个更有吸引力的地方,抵制将一切拉向仅限原生分发的引力。
重现这些结果
此比较中的所有测量都遵循严格的统计方法,旨在实现可重现性和可辩护性。每个框架每个页面使用Chrome Lighthouse测量10次,使用移动仿真(Pixel 5、4G节流、1x CPU)。服务器预热请求在测量前稳定性能。IQR(四分位距)异常值去除确保稳健统计。浏览器缓存运行间清除以测量模拟首次访问体验的冷加载性能。我报告中值以减少异常值的影响,标准差量化测量可靠性。完整方法包括统计方法、测试环境详细信息、压缩检测、已知限制和重现说明,记录在METHODOLOGY.md。
行动号召
自己尝试 :克隆仓库,构建所有十个实现,并在Chrome DevTools中的节流3G连接上测试它们。当移动网页是你唯一的选择时,数字讲述了一个清晰的故事。
不那么严肃地说:你可以查看所有十个应用,检查代码外观并感受每个框架。我主张为乐趣编码,仓库中的代码可能是帮助你尝试新事物的好地方。
分享你的经验:你尝试过Marko、SolidStart、SvelteKit、Qwik或Nuxt吗?对于移动优先项目,你会选择什么框架,为什么?我很想在Twitter或Bluesky上听到你的想法。
继续探索:完整的指标数据和测量方法可在仓库中获得,供你验证、重现或扩展。构建你自己的比较并分享你的发现。
当你用Marko、SolidStart或SvelteKit开始下一个项目时,你将提供更快、更小、框架开销更少的应用,给你真正的竞争优势。
!NOTICE\] 以下"更多阅读"由译者添加。
更多阅读
本文中提及的所有外部链接如下,供大家深入参考:
-
React Won by Default
www.lorenstew.art/blog/react-... -
项目代码仓库
github.com/lorenseanst... -
Tammy Everts at SpeedCurve (关于性能对业务影响的研究)
www.speedcurve.com/blog/downti... -
Chrome User Experience Report (CrUX)
lookerstudio.google.com/u/0/reporti... -
Progressive Complexity Manifesto
www.lorenstew.art/blog/progre... -
View Transitions API
developer.mozilla.org/en-US/docs/... -
Speculation Rules API
developer.mozilla.org/en-US/docs/... -
GitHub Blog: Our plan for a more secure npm supply chain
github.blog/security/su... -
Next.js
nextjs.org/ -
TanStack Start (React)
tanstack.com/start/lates... -
TanStack Start (Solid)
tanstack.com/start/lates... -
Nuxt
nuxt.com/ -
Analog
analogjs.org/ -
SolidStart
docs.solidjs.com/solid-start... -
SvelteKit
svelte.dev/docs/kit/in... -
Qwik City
qwik.dev/docs/qwikci... -
Astro
astro.build/ -
HTMX
htmx.org/ -
Valibot
valibot.dev/ -
Drizzle ORM
orm.drizzle.team/ -
Tailwind CSS
tailwindcss.com/ -
DaisyUI
daisyui.com/