有一种失望很难命名。用户打开你的 App,点一个「充值」「打车」或「保险」的入口,落到一个明显不是你团队做的页面上。字体不对,滚动发涩,按钮点下去空一秒,然后页面重载。还得再登一次录,登到一个他不认识的服务。没崩溃,没报错,但某种靠近信任的东西刚刚断了——而且你的崩溃日志里永远看不到它。
这行快十年,我在金融、出行、内容类 App 里反复看同一个模式。团队把原生外壳打磨得很好,然后把手里真正赚钱的那块服务——交给一个内嵌网页。结果读起来像「别人家的产品」,用户常常分不清你的 App 在哪结束、那一方从哪开始。代价落在转化和留存上,而不是可用率上。
这条缝不是一道,是三道,而且会叠。
缝在哪一层显现
渲染。webview 里的 H5 用自己的 CSS、自己的字体、自己的栅格——一门第二设计语言活在你的第一语言里。用户感觉得到这道缝,即使叫不出名字。webview 本质是浏览器引擎,它不继承你的原生组件系统、你的排版、你的动效。页面是对的,但也明显不是你的。
加载。H5 的首屏依赖网络。没有预加载,没有像 runtime 那样在用户点之前就把包预取、预置好。这很要命,因为速度不是体验指标,是漏斗。Google 自己的基准研究发现,移动网页加载超过 3 秒,53% 的访问会被放弃;Core Web Vitals(LCP 衡量绘制、INP 衡量响应)已经成了行业对「可用」的共识定义。webview 里的页面往往比真浏览器标签更慢:没有站点隔离缓存,没有宿主给自家 chrome 做的优化,冷启动纯靠网络。
能力。当服务要调相机、定位或生物识别,H5 要跨过 JS bridge,进到一个从没为它设计过的浏览器安全模型。有些调用不可靠,很多会多弹一个读起来像警告的授权框。流程自己打断了自己。宿主自有 runtime 把这些能力走审过的分级 API 暴露出来,所以同一个动作是一次原生调用,不是一场谈判。
摆到桌面上
| 维度 | 内嵌 H5(WebView) | 小程序 runtime(宿主自有) |
|---|---|---|
| 渲染一致性 | 自带样式,明显割裂 | 原生组件,一套设计语言 |
| 首屏 | 受网络约束,常转圈 | 预加载,近原生 |
| 原生能力 | bridge 受限、弹窗多 | 审过 API,原生可用 |
| 登录 | 重登第三方 | 宿主一次性登录 |
| 信任信号 | 像外链 | 沙箱托管,宿主背书 |
| 品牌归属 | 像「别人家的」 | 像亲生的 |
损害有一部分是声誉性的。内嵌页面发涩、让你重登录,用户很少怪第三方,他怪你的 App。那条一星差评写的是「这 App 很卡」,不是「这家嵌入服务很卡」。你为一段自己没做、也没法彻底修好的体验扛了品牌风险。
runtime 从另一头把缝合上。服务用原生组件和统一设计语言渲染,看起来——也用起来——像你产品的一部分。资源在点按之前就预置好,首屏不用等网络。能力由宿主提供,相机就像在你 App 别处那样打开。而且因为 runtime 是沙箱托管、宿主背书,用户的直觉是「这是 App 的一部分」——正是你想要的那条直觉。
补一句诚实的 caveat。runtime 不是烂产品的神药。它的原生组件有约束——你不能随便塞进任意 web 库——而换来信任的那个沙箱,本质就是一组限制。runtime 真正做的,是去掉你在门口交的那笔体验税:它让你不在用户点下去的那一刻、在你的服务还没机会证明自己之前,就先丢人。
这是支持 runtime 那条更安静的论点。不是「建个超级 App」。只是:别让你 App 里最重要的那块屏幕,看起来像别人的。