先交代现场:不是官方 demo,是一个日活 1.2 万的旧安卓项目
上个月帮朋友看一个本地生活 App,原包 6.8MB,安装后 27MB,日活 1.2 万左右,后台里 Android 10/11 占 63%,红米 9A 这种 3GB 内存机占 18%。老板想要一套代码同时上微信小程序、iOS、Android,还要保留扫码、极光推送、WebView 支付回调。我拿五个页面做迁移:商品列表(1000 条假数据)、订单详情、扫码、推送落地页、WebView 支付回调。测试机三台:红米 Note 12 Turbo(Android 13,12GB+256GB,骁龙 7+ Gen 2)、荣耀 9X(Android 10,4GB+64GB,麒麟 810)、iPhone 11(iOS 17.5.1)。版本也没乱写:Flutter 3.22.3 stable、React Native 0.74.5 + Hermes、UniApp 用 HBuilderX 4.24 + Vue3、Taro 4.0.8 + React 18。
我一开始也以为跨端框架选型就是看跑分,谁快选谁。结果第一周就被原生插件、构建缓存和平台审核规则打了三次脸。下面这些数是我自己机器上的,不是云评测,环境不同会有偏差,但趋势应该大差不差。
| 维度 | Flutter 3.22.3 | RN 0.74.5 | UniApp 3.0 | Taro 4.0.8 |
|---|---|---|---|---|
| release 包体积 arm64 | 11.4MB | 8.7MB | 小程序主包 1.9MB;App 7.6MB | 小程序主包 2.3MB;H5 1.1MB |
| 冷启动荣耀 9X | 1.9s | 2.4s | App 2.1s;小程序 1.3s | H5 3.6s;小程序 1.5s |
| 1000 条列表滚动平均帧率 | 58fps | 46fps | App 51fps;小程序 44fps | 小程序 42fps |
| 原生插件 | 自己写 platform channel | 社区多但版本碎 | 插件市场多,质量参差 | 偏小程序,原生能力要桥 |
| 热更新 | 自建差分/审核风险 | CodePush 系合规要小心 | 小程序天然,App 要审 | 小程序天然,App 要另做 |
| 构建耗时 M2 MacBook Air | 首次 6m12s,增量 18s | 首次 4m05s,增量 12s | HBuilderX 云打包 3m20s | 首次 2m48s,增量 9s |
我遇到的 4 个真问题,和最后怎么绕
- 荣耀 9X 上 Flutter 首次启动白屏 1.2s。不是 Dart 慢,是安装后首次加载 so 和字体。解决:在 AndroidManifest 的 application 里加
android:extractNativeLibs="false",配合--split-per-abi出 arm64-v8a,白屏降到 0.7s;但如果你有动态 feature,别乱改。 - RN 的 FlatList 在 1000 条时掉到 38fps。换
@shopify/flash-list1.6.3,设置estimatedItemSize=92,getItemType按 order/refund 分两类,帧率到 52fps。注意 Android 上removeClippedSubviews我开了反而白块,关掉。 - UniApp 主包 1.9MB 看着小,但加上 plugin 和 uni_modules 后微信开发者工具提示 2.8MB,超 2MB 限制。解决:在 pages.json 做分包,把订单详情、扫码、推送落地页拆到 subPackages;图片从 PNG 转 WebP,图标改字体;删掉没用的 uni-ui 组件。主包回到 1.63MB。
- Taro 4.0.8 编译微信小程序时,
defineConstants里塞了 process.env.NODE_ENV,开发环境正常,体验版白屏。原因是条件编译被 webpack5 摇掉了。解决:改用Taro.getEnv()判断,别在模块顶层写死。
我的独立观点:别先问性能,先算“热更新合规成本”和“原生插件债务”
很多对比文章喜欢列渲染原理,Flutter 自绘、RN 桥、UniApp 和 Taro 吃小程序容器。这些当然有用,但项目一落地,决定你下不下班的往往不是那 3-5fps,而是两件事:第一,你能不能接受热更新方案带来的审核风险;第二,Android targetSdk 35、iOS 18 SDK 每年一升,你团队有没有人愿意给原生插件擦屁股。Flutter 性能好,但 platform channel 和插件版本锁死是硬成本;RN 社区大,但 0.74 升 0.75、0.76 时,原生模块和 Metro 配置经常要重排;UniApp 和 Taro 小程序端舒服,App 端一到扫码、蓝牙、后台定位,插件市场里的东西就要你读源码。
所以我现在给朋友的决策树是这样:
- 如果小程序占 70% 以上,App 只是壳:Vue 团队选 UniApp,React 团队选 Taro。先把分包和主包压到 1.8MB 以内,别等审核被拒。
- 如果 App 占 70% 以上,且有复杂动画、长列表、离线缓存:Flutter 锁 stable,比如 3.22.3,用 fvm 管版本,
pubspec.lock必须提交。别追 beta。 - 如果已有原生团队,只想先迁 2 个页面:React Native 0.74.5 + Hermes,先把原生模块接口稳定,别一上来全量。
- 如果老板张口就要“热更新绕过审核”:先让他看应用商店规则,再决定。这个坑我见过两次,后面都是下架整改。
最后一个有点反常识的结论:包体积和冷启动可以优化,但“每个端独有的补丁量”很难压缩。你选跨端框架时,最好先算一下:微信小程序、iOS、Android、H5 四个端,每个端有多少交互是别人没有的。如果超过 30%,那所谓一套代码,最后会变成四套补丁。这个数比跑分重要。