如何实现iOS、Android、小程序和浏览器嵌套H5的跨端通信?
摘要:最近 openclaw 盛行,关注点都在这上面了,各个技术社区满屏都是 Prompt、Agent,看久了已经变得“审美疲劳”了。可能是浏览前端文章的比较少,加上最近研究 python 就没怎么发现到一些适合写的技术点,刚好项目有一些以前未熟
最近 openclaw 盛行,关注点都在这上面了,各个技术社区满屏都是 Prompt、Agent,看久了已经变得“审美疲劳”了。可能是浏览前端文章的比较少,加上最近研究 python 就没怎么发现到一些适合写的技术点,刚好项目有一些以前未熟悉(各类宿主环境 - 跨端通信)就学了一下,找了一些资料看了看,现在整理一下。
网上虽然有很多 JSBridge 的文章,但往往只针对单端。在实际的一些项目里,同一套 H5 往往要“一套代码,处处运行”。基于 Vue3 移动端 H5,梳理环境识别、统一桥接层、iOS WKWebView / Android Uni WebView / 微信小程序 web-view / 普通 H5 的通信方式,涉及的有 window.webkit、uni.postMessage、jweixin、window.location,不熟的可以上网看看资料。
一、为什么要做“统一桥接层”?
“Write once, run anywhere” 对于纯展示型 H5 是成立的。但只要涉及到业务交互,比如:调起原生登录、保存图片到相册、修改系统状态栏颜色、分享到朋友圈,浏览器标准的 Web API 根本无能为力。
此时,H5 的宿主环境通常有这几种:
自有 iOS App(底层是 WKWebView)
自有 Android / 鸿蒙 App(可能是原生 WebView,也可能是 UniApp 壳子嵌套)
微信小程序(底层是 <web-view> 组件)
微信内置浏览器(公众号 H5,依赖 JSSDK)
纯外部浏览器(Safari、Chrome 等)
如果让前端业务组件直接去写 if (isIOS) {...} else if (isWechat) {...},代码不出三个月就会变成一座屎山。因此,我们需要一层 Adapter(适配器),业务侧只管调用 bridge.toLogin(),具体怎么发消息,交给桥接层内部去分发。
二、环境探测:一切通信的前提
要精准分发,首先要知道自己在哪。主流的做法是依托 window.navigator.userAgent(配合端上约定的特殊标识)。
export const getEnvironment = () => {
const ua = navigator.userAgent.toLowerCase();
if (ua.includes('micromessenger')) {
if (ua.includes('miniprogram') || window.__wxjs_environment === 'miniprogram') {
return 'miniprogram'; // 微信小程序
}
return 'wechat'; // 微信内置浏览器(公众号)
}
// 假设 Android 端是用 UniApp 打包的,通常会有 Html5Plus 标识
if (ua.includes('html5plus') || ua.includes('uni-app')) return 'android_uni';
// 假设自有 iOS 原生开发,约定了特殊的 UA 后缀,如 "MyApp/iOS"
if (ua.includes('iphone') && ua.includes('myapp')) return 'ios_native';
// 假设鸿蒙端约定了 OpenHarmony 标识
if (ua.includes('openharmony')) return 'harmony';
return 'h5'; // 兜底:纯普通浏览器
};
踩坑提示:UA 是可以被伪造的。环境判断仅作为“前端体验分支”的依据。涉及发券、支付等核心安全逻辑,必须由服务端通过 Token/Cookie 进行最终鉴权。
三、各端通信底层原理与官方规范
知其然也知其所以然,先来扒一扒各个环境底层的通信机制。
1. iOS (WKWebView)
参考苹果官方文档,现代 iOS 均使用 WKWebView。前端向原生发消息的标准写法是:
window.webkit.messageHandlers.<约定好的方法名>.postMessage(<参数>)
特性:只能前端单向调用原生。原生如果想回传结果,只能通过 evaluateJavaScript 直接执行一段全局挂载的 JS 函数(比如 window.onLoginSuccess(token))。
