React、Vue、Svelte、Solid 怎么选?我用三个真实项目踩完坑后的对比表
先给结论,再给账本。这篇文章里除了官方公布的日期和体积,所有数字都是我自己机器上量出来的,换了机器会变,所以第四节我把测量步骤完整写出来了,你自己跑一遍比信我更有用。
一、我现在评判框架只看一个指标:升级摩擦率
2019 年我做的第一个能上线收钱的项目是 Vue 2.6 + Element UI 的后台系统,87 个路由页面。2023 年 12 月 31 日 Vue 2 正式 EOL,我们做了一轮迁移评估,结论是 47 人天——3 个前端,含 Options API 改 Composition API 的机械工作、element-ui 换 element-plus 的样式回归、还有 4 个只支持 Vue 2 的图表封装要重写。同期手上另一个 React 16.8 的项目升到 18,只花了 6 人天,主要成本是把 ReactDOM.render 换成 createRoot,剩下的是 StrictMode 双调用暴露出来的两个副作用 bug。
这两个数字摆在一起之后,我就不太看 GitHub stars 了。我现在用的尺子是:
升级摩擦率 = 一次大版本迁移的人天 ÷ 项目预期寿命(年)
Vue 2 那个项目如果还能再活 5 年,摩擦率就是 47 / 5 ≈ 9.4 人天/年,其实不低。React 16→18 是 6 / 5 = 1.2。这个算法还漏算了一个隐性成本:迁移期间业务需求在排队,产品不会因为你在换框架就停下来,那三周里积压的 11 个需求最后是加班补的。所以现在接新项目,我第一个问题不是「哪个性能好」,而是「三年后谁给我做迁移」。
二、先把体重秤摆出来:四家的基线数字
| 维度 | React 19 | Vue 3.5 | Svelte 5 | Solid 1.9 |
|---|---|---|---|---|
| 运行时 min+gzip | 约 44KB(react + react-dom) | 约 34KB(runtime-only) | 我本地带列表页面构建约 12KB | 约 7KB |
| 更新粒度 | 组件级,靠 memo/useMemo 手动收窄 | 组件级 + 编译期 PatchFlag 标记动态节点 | 细粒度,runes 直接绑 DOM 节点 | 细粒度,无 VDOM |
| 心智模型 | 函数重新执行 + 依赖数组 | 响应式代理 + 模板编译 | 编译期改写赋值语句 | 编译期生成 DOM 操作 |
| 大版本时间 | 19 于 2024-12-05 稳定 | 3.5 | 5 于 2024-10-19 稳定 | 1.9 |
| 生态/招人 | 最厚,简历最多 | 国内最厚 | 中等,成熟 UI 库偏少 | 薄 |
Svelte 官网挂的 Hello World 1.6KB 是 Svelte 4 时代的数字,5 引入 runes 之后运行时变大了。我本地跑一个 50 行的待办列表,构建产物 gzip 后大概 12KB,大概是 Solid 的两倍,但仍然是 React 的四分之一左右。这三个数字摆一起你会发现:包体积这件事,早就不在同一个量级上比了,还在纠结 44KB 还是 34KB,不如去看看你 node_modules 里有没有全量引入的 lodash。
Angular 我不在这篇里写,只用过两个月,没资格装专家。
三、真正的分水岭是更新粒度,不是包体积
React 的模型是:setState → 该组件函数重新执行 → 产出新 VDOM → diff → 提交。默认情况下父组件更新会把子组件一起重新执行,除非你手动 memo。我在 M1 Pro / Chrome 131 上量过一个 1000 行、每行 5 个字段的表格:不加 React.memo 时,输入框每敲一个字符,commit 阶段约 14ms,连续输入能明显感觉粘手;加 memo + useCallback 之后降到 2.3ms。注意这还是没开 React Compiler 的情况。
Svelte 5 和 Solid 是编译期就决定了「哪个 DOM 节点绑哪个变量」,改一个 count,编译器生成的代码里就只更新那一个文本节点,谈不上 diff 成本。在 krausest 那个 js-framework-benchmark 的 keyed swap 用例里,同一台机器上 Solid 和 Svelte 通常打到个位数毫秒,React 常见落在 20-40ms 区间。
Vue 3 站在中间:模板编译时给动态节点打 PatchFlag,运行时用 Proxy 收集依赖,粒度是组件 + 动态节点,你不用手写 memo,但也细不到单个文本节点。这个差异只在「大量静态数据 + 少量高频交互」的页面(后台看板、在线表格、代码编辑器)才真正咬人。普通官网首页,四家你根本量不出差别——我试过。
四、手把手:30 分钟测出你自己项目的账
别抄别人的 benchmark,你自己的项目才是真的。
步骤 1,量体积(5 分钟)
pnpm build
npx vite-bundle-visualizer
# webpack 项目用:
npx source-map-explorer 'dist/assets/*.js'
你大概率会有一个和我一样的发现:moment.js + lodash 全量引入比框架运行时本身还大。我上个项目 vendor 里这两个加起来 210KB gzip,换成 dayjs + lodash-es 之后掉到 38KB,比换框架省事多了。
步骤 2,量 hydration 成本(10 分钟)
Chrome DevTools → Performance → 录制一次刷新 → 看主线程有没有超过 200ms 的长任务。Lighthouse 里 TBT(Total Blocking Time)的阈值是 200ms 以内,超过就说明用户点页面的时候确实有延迟。
步骤 3,量 INP(10 分钟)
import { onINP } from 'web-vitals';
onINP(({ value, attribution }) => {
navigator.sendBeacon('/api/vitals', JSON.stringify({
inp: Math.round(value),
target: attribution?.eventTarget
}));
});
INP 良好线是 200ms 以下。如果 INP 高但 TBT 不高,问题多半在你的事件处理函数里,不在框架。
步骤 4,量迁移摩擦(半天,但最值)
翻最近 12 个月的 git log,统计有多少次改动是被「升级依赖」逼出来的,把 commit 时间和加班日期对上。这个数字比你想象的更能说明问题。
五、说点反常识的:这些性能数字,对 90% 的项目不重要
我统计过手上 7 个项目的 LCP 瓶颈,只有 2 个和框架相关。剩下 5 个的瓶颈分别是:一张没压缩的 1.2MB 首页大图、一次串行的三个接口请求、一个 800ms 才加载完的自定义字体,还有两个是 CDN 没配。
44KB 的运行时在 4G 网络(有效带宽约 1.5MB/s)下大概 30ms 就下完了,而那张大图要 0.8 秒。你花两周把 React 换成 Solid,可能不如花两天把 PNG 换成 WebP 再挂个 CDN。所以我给自己定了条规矩:首屏 LCP 超过 2.5s,且图片和请求都排查完了,才允许讨论换框架。
六、我改过主意的一件事
2022 年我很迷 Solid,在一个 2 人的数据看板项目里用了。性能确实爽,但半年后我把它重写成了 Vue 3。
原因不是技术。那个项目后来要加一个支持虚拟滚动的树形选择器、一个能拖拽的甘特图、一个富文本编辑器。Solid 生态里这些要么没有、要么是个人维护的 0.x 版本,我得自己包一层,粗算每月多花 6-10 小时。更现实的是:我在招聘 JD 里写 SolidJS 经验优先,两周收到 0 份匹配简历;改成 Vue 3,当天来了 11 个。
技术选型的天花板从来不是技术,是你的团队规模和组织能力。5 人以下的团队,选「招得到人 + 生态买得到轮子」的框架,收益远大于那 10ms 的渲染差距。
七、我的选型清单
- [ ] 团队现在最熟哪个?熟练度在前 6 个月带来的收益,远大于框架本身差异
- [ ] 未来 3 年要不要 SSR/SEO?要的话看 Next.js / Nuxt / SvelteKit / SolidStart 的成熟度
- [ ] 你需要的 3 个核心组件(表格/图表/富文本),生态里有没有维护活跃的现成轮子
- [ ] 有没有人在用、踩过坑、能在群里问(国内 Vue 社区明显更活跃)
- [ ] 这个项目大概率会怎么死:死于性能,还是死于没人维护
最后
上面所有数字,除了发布日期和官方体积,都是我在自己机器和项目上量的,换台机器、换个网络、换个版本,数字全变。别把表格当结论,把第四节的步骤拿去跑一遍你自己的项目,那 30 分钟比看 10 篇对比文章都值。
顺便说一句,写完这篇我又量了一下手上这个 React 19 项目,TBT 是 340ms,瓶颈是 hydration 阶段加载的一个第三方客服 SDK。所以你看,问题往往不在框架。