在移动应用开发中,UniApp 凭借其跨平台能力和 Vue 语法优势,成为许多团队的首选框架。然而,当项目需要集成复杂的原生功能(如高性能图形渲染、特定硬件调用或已有原生模块)时,纯 UniApp 方案往往力不从心。这时,混合开发模式——将 UniApp 页面嵌入原生 App 的 WebView 中,或反过来在 UniApp 中嵌入原生页面——便成为一种务实的选择。
本文将聚焦混合开发中最核心的两个环节:WebView 通信与页面跳转,结合实战场景给出可落地的方案。
一、混合开发的两种典型架构
在讨论通信与跳转之前,先明确两种常见架构:
- 原生为主,UniApp 为辅:原生 App 作为宿主,通过 WebView 加载 UniApp 打包后的 H5 页面。适合已有成熟原生 App,需要快速迭代部分业务模块的场景。
- UniApp 为主,原生为插件:UniApp 作为主框架,通过
plus.webview或原生插件嵌入原生页面。适合以 UniApp 为主但需要调用特定原生能力的场景。
本文主要针对第一种架构展开,因为它的通信与跳转问题最为典型。
二、WebView 通信:双向数据传递
WebView 通信的本质是原生与 JavaScript 之间的消息传递。在 UniApp 混合开发中,通常涉及三个角色:原生 App、WebView 容器、UniApp H5 页面。
2.1 原生向 UniApp 发送消息
原生端可以通过 evaluateJavascript(Android)或 stringByEvaluatingJavaScriptFromString(iOS)直接调用 WebView 中的全局函数。
Android 示例:
webView.evaluateJavascript("javascript:onNativeMessage('" + jsonData + "')", null);
iOS 示例:
webView.evaluateJavaScript("onNativeMessage('\(jsonData)')") { result, error in }
在 UniApp 页面中,需要提前挂载全局函数:
// 在 UniApp 的 H5 页面中
window.onNativeMessage = function(data) {
const message = JSON.parse(data);
// 处理原生传来的数据
console.log('收到原生消息:', message);
};
2.2 UniApp 向原生发送消息
UniApp 向原生发送消息有三种主流方式:
方式一:URL Scheme 拦截(通用性最强)
UniApp 通过修改 window.location.href 触发原生端的 URL 拦截:
// UniApp 端发送
const message = { type: 'login', token: 'abc123' };
window.location.href = `myapp://native?data=${encodeURIComponent(JSON.stringify(message))}`;
原生端拦截该 URL 并解析:
// Android WebViewClient
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("myapp://")) {
// 解析 url 中的参数,执行原生逻辑
return true;
}
return false;
}
方式二:注入对象(Android addJavascriptInterface)
Android 端通过 addJavascriptInterface 注入原生对象:
webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");
class NativeBridge {
@JavascriptInterface
public void postMessage(String data) {
// 处理来自 UniApp 的消息
}
}
UniApp 端直接调用:
window.NativeBridge.postMessage(JSON.stringify({ type: 'pay', orderId: '123' }));
方式三:WKWebView 的 messageHandler(iOS 推荐)
iOS 端配置:
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "nativeBridge")
UniApp 端调用:
window.webkit.messageHandlers.nativeBridge.postMessage({ type: 'share', content: '...' });
2.3 通信封装建议
为了降低耦合,建议在 UniApp 侧封装统一的通信模块:
// bridge.js
class NativeBridge {
static send(type, data = {}) {
const message = { type, data, timestamp: Date.now() };
// 根据运行环境选择发送方式
if (window.NativeBridge) {
window.NativeBridge.postMessage(JSON.stringify(message));
} else if (window.webkit?.messageHandlers?.nativeBridge) {
window.webkit.messageHandlers.nativeBridge.postMessage(message);
} else {
// 降级为 URL Scheme
window.location.href = `myapp://bridge?data=${encodeURIComponent(JSON.stringify(message))}`;
}
}
static receive(callback) {
window.onNativeMessage = (data) => {
try {
callback(JSON.parse(data));
} catch (e) {
console.error('消息解析失败', e);
}
};
}
}
三、页面跳转:原生与 UniApp 的导航协同
页面跳转比通信更复杂,因为它涉及两个独立的路由系统:原生的 Activity/ViewController 栈和 UniApp 的页面栈。核心原则是:谁主导跳转,谁负责维护栈的一致性。
3.1 原生跳转到 UniApp 页面
原生端直接加载对应的 UniApp H5 页面 URL,并可通过参数传递初始数据:
// Android
String url = "https://your-h5-domain.com/#/pages/order/detail?id=123";
webView.loadUrl(url);
UniApp 页面在 onLoad 中接收参数:
onLoad(options) {
const orderId = options.id;
// 根据 orderId 加载数据
}
注意:如果 UniApp 使用 hash 路由,参数应放在 # 之后;如果使用 history 模式,则按普通 URL 参数处理。
3.2 UniApp 跳转到原生页面
UniApp 无法直接操作原生页面栈,必须通过通信机制请求原生执行跳转:
// UniApp 端
NativeBridge.send('navigate', {
page: 'NativeOrderDetail',
params: { orderId: '123' }
});
原生端收到消息后执行:
// Android
Intent intent = new Intent(context, OrderDetailActivity.class);
intent.putExtra("orderId", orderId);
startActivity(intent);
3.3 跳转中的栈管理策略
混合开发中最容易出问题的是返回逻辑。建议遵循以下策略:
- 原生 → UniApp → 原生:UniApp 页面应作为原生栈中的一个“黑盒”,返回时由原生统一处理。
- UniApp 内部跳转:使用
uni.navigateTo等 API,不要触发原生跳转,除非确实需要离开 WebView。 - 返回拦截:在 UniApp 中监听
onBackPress,必要时通过通信通知原生执行特定返回逻辑。
// UniApp 中拦截返回
onBackPress(options) {
if (this.hasUnsavedChanges) {
NativeBridge.send('confirmBack', { message: '有未保存的内容,确定返回?' });
return true; // 阻止默认返回
}
return false;
}
四、实战注意事项
- URL Scheme 冲突:自定义 Scheme 要确保全局唯一,避免与其他 App 冲突。
- 消息序列化:复杂对象必须 JSON 序列化,注意循环引用和特殊字符转义。
- 线程安全:Android 的
addJavascriptInterface回调运行在非 UI 线程,操作 UI 需切回主线程。 - iOS WKWebView 内存:大量消息传递可能导致内存上涨,及时移除不再使用的 messageHandler。
- 调试技巧:Android 用
chrome://inspect,iOS 用 Safari 开发者工具,可大幅提升排查效率。
五、总结
UniApp 与原生 App 的混合开发,通信是血脉,跳转是骨架。通信方案的选择取决于团队技术栈和兼容性要求:URL Scheme 最通用,注入对象最直接,messageHandler 最规范。页面跳转则需明确“谁主导、谁维护栈”的原则,避免出现返回逻辑混乱。
在实际项目中,建议将通信和跳转逻辑封装为独立的 Bridge 模块,统一管理消息格式和错误处理。这样既能降低维护成本,也能在后续扩展新功能时保持架构清晰。混合开发不是妥协,而是一种务实的工程选择——用对地方,它能发挥出 1+1>2 的效果。
未经允许不得转载:任鹏个人博客 » UniApp 与原生 App 混合开发:WebView 通信与页面跳转


朋友圈点赞图在线生成源码