Skip to content
Go back

一个 JS Bridge 库要怎样处理版本兼容

JS Bridge 的版本兼容问题,来自 H5 和客户端完全不同的发布节奏。

H5 页面可以随时发布,今天改完今天上线。客户端 App 需要发版、审核、用户升级,线上会长期存在多个版本。于是很容易出现这种情况:

H5 已经调用了新 Bridge 方法但一部分用户的客户端还不支持

如果 Bridge SDK 没有处理好兼容,业务页面会出现各种问题:按钮点了没反应、分享失败、登录调不起来、图片选择异常、页面跳转不到原生页。

所以一个成熟的 JS Bridge 库,不只是把消息送到客户端,还要处理版本差异。

不要只用 App 版本判断能力

最容易想到的做法是判断 App 版本:

if (appVersion >= "8.5.0") {  bridge.call("image.choose");}

这种方式能用,但不够稳定。

原因有几个:

更可靠的是能力探测。

H5 不问“你是不是 8.5.0”,而是问“你支不支持这个方法”:

const supported = await bridge.canIUse("image.choose");if (supported) {  await bridge.call("image.choose");} else {  showUploadFallback();}

客户端提供能力表:

{  "bridgeVersion": "2.4.0",  "platform": "ios",  "methods": {    "share.openPanel": {      "since": "1.0.0"    },    "image.choose": {      "since": "2.1.0"    },    "navigation.openPage": {      "since": "1.0.0"    }  }}

Bridge SDK 初始化后缓存能力表,业务调用前通过 canIUse 判断。

SDK 要有初始化状态

Bridge SDK 不是页面一加载就一定可用。客户端注入对象可能比业务 JS 执行更晚,尤其是一些 Android WebView 或异步注入场景。

因此 SDK 要有 ready 机制:

await bridge.ready();await bridge.call("user.getLoginInfo");

ready 做几件事:

等待客户端注入完成获取 Bridge 能力表记录平台、App 版本、Bridge 协议版本初始化 callback 和事件分发

如果超时仍然没有 ready,SDK 要明确进入不可用状态:

try {  await bridge.ready({ timeout: 3000 });} catch (error) {  renderWebFallback();}

业务代码不应该自己判断 window.NativeBridge 是否存在。这个判断应该被 SDK 收敛。

API 设计要默认支持降级

Bridge SDK 对业务暴露的 API,最好天然包含降级意识。

例如分享能力:

await bridge.share({  title,  url,  fallback() {    showCopyLinkPanel();  }});

内部逻辑可以是:

检查 Bridge 是否 ready检查 share.openPanel 是否支持调用客户端分享如果失败或不支持,执行 fallback

这样业务接入时不会每次都手写一堆兼容判断。

也可以提供通用调用:

bridge.call("share.openPanel", params, {  fallback: () => showCopyLinkPanel(),  timeout: 5000});

SDK 的目标不是让业务“能调到客户端”,而是让业务在不同客户端版本下都有稳定行为。

协议字段只能向前兼容

Bridge 兼容性最容易出问题的是协议字段变化。

比如一个方法最初这样设计:

{  "method": "navigation.openPage",  "params": {    "url": "app://car/detail?id=123"  }}

后来希望支持无动画跳转,不应该改变 url 的含义,而应该新增可选字段:

{  "method": "navigation.openPage",  "params": {    "url": "app://car/detail?id=123",    "animated": false  }}

旧客户端不认识 animated 时可以忽略,新客户端可以使用它。这就是向前兼容。

不推荐的改法是:

{  "method": "navigation.openPage",  "params": {    "url": {      "value": "app://car/detail?id=123",      "animated": false    }  }}

这会改变 url 的类型,旧客户端解析时很可能直接失败。

协议演进要遵守:

能新增可选字段,就不要改旧字段能新增 method,就不要改变旧 method 语义旧字段可以废弃,但不要马上删除客户端解析未知字段时应忽略H5 调用新字段时必须准备降级

方法废弃需要有过渡期

Bridge 方法一旦被业务页面使用,就不能随意删除。因为线上可能存在旧 H5、旧活动页、缓存页,也可能有用户长期停留在旧客户端版本。

废弃方法可以分阶段:

阶段一:标记 deprecated,但继续可用阶段二:SDK 开发环境 warning阶段三:业务迁移到新方法阶段四:客户端保留兼容转发阶段五:确认线上无调用后再下线

例如旧方法:

bridge.call("share", params);

新方法:

bridge.call("share.openPanel", params);

客户端可以在一段时间内兼容:

share -> share.openPanel

SDK 也可以在开发环境提醒:

[Bridge] method "share" is deprecated, use "share.openPanel" instead.

这种兼容看起来麻烦,但比线上页面突然不可用要可靠得多。

不同端能力要保持同名同义

Bridge 方法如果在 Android 和 iOS 上同名,就必须保证语义一致。

例如:

image.choose

如果 iOS 返回:

{  "localId": "xxx",  "width": 100,  "height": 100}

Android 返回:

{  "path": "file://xxx"}

那业务页面就被迫写平台判断,Bridge SDK 的意义会被削弱。

更好的方式是 SDK 或客户端统一返回结构:

{  "images": [    {      "id": "xxx",      "uri": "file://xxx",      "width": 100,      "height": 100    }  ]}

如果平台确实有差异,也应该放在明确字段里:

{  "platformExtra": {}}

但主路径返回结构要一致。

SDK 要内置错误码映射

客户端返回的错误码如果没有规范,业务会很难处理。

SDK 应该把底层错误统一成稳定错误类型:

try {  await bridge.call("image.choose");} catch (error) {  if (error.code === "METHOD_NOT_SUPPORTED") {    showFallback();  }}

底层可能来自不同端:

iOS: -1001Android: 404Scheme: timeout

SDK 对外统一成:

METHOD_NOT_SUPPORTEDINVALID_PARAMSPERMISSION_DENIEDUSER_CANCELBRIDGE_TIMEOUTNATIVE_ERROR

这样业务代码不需要知道不同端内部错误码。

灰度能力要能被识别

客户端能力有时不是随着版本全量发布,而是灰度开放。

例如 8.8.0 版本里只有 20% 用户支持新的分享面板。如果 H5 只判断版本,就会误以为所有 8.8.0 都支持。

能力表可以包含灰度结果:

{  "methods": {    "share.openNewPanel": {      "supported": true,      "since": "2.5.0",      "gray": true    }  }}

H5 只根据 supported 判断,不直接根据版本猜。

这对客户端渐进发布非常重要。Bridge SDK 要承认线上环境不是整齐的,能力支持情况可能因用户、端、版本、实验分组而不同。

调试和监控是兼容性的保障

版本兼容问题很难靠开发阶段完全覆盖。线上一定会遇到各种组合:

新 H5 + 旧 App旧 H5 + 新 App灰度 App + 新 Bridge SDK低版本 WebView + 新页面缓存页面 + 已废弃方法

所以 SDK 要上报关键指标:

这些数据能帮助判断一个新 Bridge 能力是否可以扩大使用,也能发现某个客户端版本是否存在实现问题。

开发阶段则需要调试面板:

Bridge Version: 2.4.0App Version: 8.9.0Platform: AndroidMethods: 42[success] user.getLoginInfo 42ms[failed] image.choose METHOD_NOT_SUPPORTED[timeout] share.openPanel 5000ms

没有调试和监控,Bridge 兼容性问题只能靠用户反馈,定位成本会很高。

小结

JS Bridge 库的版本兼容,本质上是在调和两个发布节奏:H5 快,客户端慢。

一个可靠的 Bridge SDK 应该做到:

JS Bridge 的设计不能只面向当前版本。它必须默认面对混乱的线上版本分布。只有把兼容性设计进 SDK 和协议里,移动端 H5 才能稳定使用客户端能力。


Share this post on: