
混合架构适合快速迭代与跨平台交付,但 Web 与原生之间的交互一旦设计不当,就会在时序、兼容、安全与性能上频繁踩坑。本文以前端视角给出一套可落地的交互设计与避坑清单,帮助你在 iOS 与 Android 的 WebView 环境中稳定上线。
type、payload、callbackId、timestamp,避免多种格式并存。{
"type": "openCamera",
"payload": {"quality": 0.8},
"callbackId": "c_172931",
"timestamp": 1732520000
}ready 事件保证调用前桥已可用。callbackId,在回调表中存储,原生完成后根据 callbackId 精确回调。const Bridge = (() => {
const callbacks = new Map()
const genId = () => 'cb_' + Date.now() + '_' + Math.random().toString(16).slice(2)
const post = msg => {
if (window.AppBridge && typeof window.AppBridge.postMessage === 'function') {
window.AppBridge.postMessage(JSON.stringify(msg))
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.AppBridge) {
window.webkit.messageHandlers.AppBridge.postMessage(msg)
}
}
const callNative = (type, payload = {}, {timeout = 8000} = {}) => new Promise((resolve, reject) => {
const id = genId()
const timer = setTimeout(() => {
callbacks.delete(id)
reject(new Error('TIMEOUT'))
}, timeout)
callbacks.set(id, res => {
clearTimeout(timer)
callbacks.delete(id)
if (res && res.error) reject(new Error(res.error))
else resolve(res)
})
post({type, payload, callbackId: id, timestamp: Date.now()})
})
const onNative = data => {
const {callbackId} = data || {}
if (callbackId && callbacks.has(callbackId)) callbacks.get(callbackId)(data)
}
return {callNative, onNative}
})()
window.Bridge = BridgewebView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
webView.addJavascriptInterface(new AppBridge(activity), "AppBridge");public class AppBridge {
private final Activity activity;
public AppBridge(Activity a){ this.activity = a; }
@JavascriptInterface
public void postMessage(String json){
// 解析 json 并分发,完成后回调 onNative
}
}private void sendToWeb(WebView webView, String json){
webView.post(() -> webView.evaluateJavascript("Bridge.onNative(" + json + ")", null));
}DomStorage 与合适的 mixedContent;谨慎使用 addJavascriptInterface,只暴露必要对象与方法。let config = WKWebViewConfiguration()
let ucc = WKUserContentController()
ucc.add(self, name: "AppBridge")
config.userContentController = ucc
let webView = WKWebView(frame: .zero, configuration: config)func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "AppBridge" {
// 解析 message.body,处理后回调到 JS
}
}let script = "Bridge.onNative(" + jsonString + ")"
webView.evaluateJavaScript(script)WKUserScript 在合适时机注入桥对象。DOMContentLoaded 后检测桥是否可用,否则延迟或轮询;原生在创建 WebView 前注入桥。UIImagePickerController 与相册授权。onBackPressed,优先让 Web 处理路由栈;iOS 通过导航委托判断是否返回或下发事件给 Web。eval 与字符串拼装执行;输入校验与域名白名单;桥能力最小化并记录审计。Bridge.callNative('pickImage', {source: 'camera'}).then(res => {
const {url} = res
})function onPushMessage(data){
const {payload} = data
}document.addEventListener('DOMContentLoaded', () => {
if (window.Bridge) {
}
})chrome://inspect,iOS 使用 Safari 开发者工具;为桥层加上最小日志。结论:混合架构的“坑”多源自协议与时序设计不严谨。把交互能力工程化为可测试、可监控的桥层,并用统一的协议与质量保障覆盖端上差异,前端就能以较低成本稳定地利用原生能力,同时保持跨平台的交付效率。