同一产品需要覆盖 PC、App 内 H5 和小程序时,“一套代码多端运行”听起来是最自然的目标。实际开发中,各端的路由、生命周期、权限、存储和交互习惯都不同。强行抹平差异,会把平台判断散落到业务代码里。

复用之前,应该先明确哪些层不应复用。视图和平台能力往往变化最快,领域规则、接口模型和数据转换更稳定。

用适配层隔离平台

相机、分享、登录和返回行为可以通过小而明确的适配接口暴露。业务代码依赖“选择图片”或“获得身份”,而不是直接判断当前是否在微信或 App WebView。

适配层不能假装所有平台能力完全一致。某端不支持的能力应该明确返回不可用,让产品决定降级方式,而不是静默失败。

例如选择图片不应该返回一个各端都含义不同的字符串:

type PickedImage = {
  localUri: string;
  mimeType?: string;
  sizeBytes?: number;
};
 
type CapabilityResult<T> =
  | { ok: true; value: T }
  | { ok: false; reason: "unsupported" | "denied" | "cancelled" | "failed" };
 
interface MediaCapability {
  pickImage(options: { maxCount: number }): Promise<CapabilityResult<PickedImage[]>>;
}

小程序返回临时路径,Web 返回 File,App Bridge 可能先返回资产 ID。适配器负责把这些结果变成业务可理解的最小结构;如果上传仍需平台对象,则把上传也留在适配器内,不要泄漏半转换状态。

复用要计算维护成本

共享一段代码节省了首次开发,却可能增加测试组合和发布耦合。如果两个端的迭代节奏不同,过度共享会让小改动触发全端回归。

我更愿意复用稳定规则,而不是追求文件层面的统一。允许少量视图重复,换取清晰的平台边界,通常比一个充满条件分支的“万能组件”更便宜。

测试矩阵要跟着边界走

跨端项目的测试数量很容易相乘。所有功能在所有平台、所有版本上完整回归并不现实。我们按共享层和平台层设计测试:领域规则运行一套稳定用例,适配器针对每个平台验证契约,关键用户路径再覆盖真实设备组合。

Web App H5 小程序
领域规则 同一套单元测试 同一套 同一套
能力适配器 浏览器权限与 File Bridge 版本与回调 授权弹窗与临时路径
关键路径 主流浏览器 最低支持 App 版本 当前与上一基础库版本

这样新增一个业务校验不需要全端重复验证,修改 Bridge 协议却会明确触发 App H5 的兼容回归。测试数量跟随边界,而不是跟随仓库文件数。

发布也要记录各端版本对应的协议能力。H5 可以快速更新,App 与小程序存在审核和用户升级延迟,前端不能只按照最新客户端假设。兼容范围如果没有被写出来,就会以线上偶发错误的方式出现。

跨端架构的目标不是最高复用率,而是让差异可见、可控制。承认平台不同,才有可能真正共享值得共享的部分。

业务能力协议连接 Web、Native 和小程序适配的跨端架构

图:共享的是业务语义和协议,不是强行共享所有实现。

先做能力矩阵,再讨论复用比例

我们会把登录、返回、分享、上传、支付和文件预览列成矩阵,逐端写输入、输出、权限、超时和降级。只有语义一致的能力共享接口,纯视觉和平台特有体验允许分开。

能力 Web App WebView 小程序
登录 Cookie 会话 Bridge 换短期票 平台 code 换会话
分享 Web Share/复制 原生分享面板 平台分享钩子
失败 页面提示 Bridge 错误码 平台回调

这张矩阵比“复用 80%”更能预测真实成本。