每个在多个平台发过版的产品负责人都懂那笔安静的税。同一个登录页、同一个结账、同一个设置页——写三遍,三种语言,三个团队,永远没法完全对齐。iOS 用 Swift,Android 用 Kotlin,Web 用 TypeScript。一个功能变成三份工程预算、三个 bug 队列、三条越走越远的发布列车。
我见过团队把最好的人耗在让三套实现保持同步,而不是去做下一件事。这份冗余不是一次性成本,它会复利。
三个面的算术
移动市场不让你选覆盖。Android 在北美之外拿下全球绝大多数份额;iOS 主导高价值的北美和日本市场;而 Web 兜底桌面和可分享链接,是桌面端的入场券。StatCounter 的数据把 Android 放在全球移动使用量 70% 以上、iOS 在十几个百分点——你没法靠一个平台服务市场。于是你养了三套技术栈。
成本在三处显现。第一,构建成本:同一套逻辑重写三遍,每遍都带平台特有的 bug。第二,维护:iOS 上修好的缺陷,在 Android 和 Web 上还活著,直到有人发现。第三,漂移:三个版本行为悄悄分叉,于是你的「一个产品」在换设备的用户眼里并不一致。
摆到桌面上
| 维度 | 原生 iOS + Android + Web | 通用 runtime(一套代码) |
|---|---|---|
| 工程栈 | 三套(Swift/Kotlin/TS) | 一套 |
| 构建成本 | 功能 × 3 | 功能 × 1 |
| bug 面 | 三倍、平台特有 | 单一、共享 |
| 发布节奏 | 三趟列车、漂移 | 一条流水线 |
| 触达 | 必须覆盖所有平台 | 跨宿主/平台运行 |
| 团队规模 | 三名专家 | 一个跨端团队 |
通用 runtime 改的是工作单元。你针对一个 runtime 把服务写一次,它在每个宿主和平台上一致地提供身份、支付和 UI 组件。那些原生差异——按钮在 iOS 和 Android 上长什么样——由 runtime 的平台适配器处理,而不是你重写一遍。开放目录模型意味着同一个小程序跑在许多宿主 App 里,你不用为每个宿主移植一遍。
诚实的 caveat:runtime 不是零成本。平台原生能力仍需映射,你用三套完全定制栈的自由,换了一套 runtime API 面的约束。但这笔交易把结构性成本——所有东西写三份——变成可管理的一份。对大多数团队,这差别就是「发得出去」和「发不出去」。
如果你养了三个团队,只为用三种方式说同一件事,问题不是「用哪个跨端工具」,而是「我们为什么要付钱说三遍」。