介绍背景
本文主要介绍Web(JS)和 Native(App)是如何“跨语言、跨环境”通信的
在移动端盛行的年代,纯原生开发和纯 Web 开发各有优劣:
1.原生语言(iOS/Android),性能高、功能覆盖全面,但是开发效率低,每次改动都要重新打包发布
2.Web H5开发跨平台,发布更新方便,但是性能较低,受限于浏览器或 WebView 的能力
所以出现了 混合开发(Hybrid) :
- 理念:用 JS/HTML/CSS 做业务逻辑和 UI,用原生提供底层能力
- 目标:兼顾开发效率、跨平台和性能
但是在混合开发(Hybrid App)中,页面(JS)运行在 WebView 里,而业务能力却掌握在原生 App 手中。
业务上需要:
打开相册
获取定位
调起支付
登录态同步
- 但是JS 无法直接调用原生 API ,原生也不能直接访问 JS 运行时
这就带来了一个核心问题:
WebView 和 Native 是两个完全不同的运行环境,它们如何通信?
JSBridge,就是为了解决 Web ↔ Native 通信问题而诞生的。
什么是JS bridge
JSBridge 是一套运行在 WebView 中的通信机制, 用于实现 JavaScript 与 Native 之间的双向调用。
简单的说:JSBridge 是混合开发开发中,JS 调用原生能力的“桥梁” 。
它在 APP 内方便地让 Native 调用 JS,JS 调用 Native ,是双向通信的通道。
总结一句话:
- WebView 是 JS 的沙箱环境,JSBridge 是 JS 与原生通信的桥梁
JSbridge主要包含什么?
从通信方向上看,JSBridge 并不复杂, 它本质上只解决了两件事:
- 1.JS 如何调用 Native 能力
- 2.Native 如何反向调用 JS
也就是我们常说的:
- 1.JS Call Native
- 2.Native Call JS
下面分别从这两个方向展开。
JS Call Native
Jsc Call Native是Web 页面向原生请求能力。
目前市面上的主流实现方案有以下几种,我们分别介绍
- 1.URL Scheme 拦截
- 2.prompt / alert 拦截
- 3.WebView 注入对象(主流方案)
- 4.消息队列 + 回调机制 (工业级方案)
1.URL Scheme 拦截
原理: WebView 可以加载 URL,而原生可以拦截 URL 的加载过程。
我们把通信伪装成一次页面跳转
在这个方案中:
- 1.JS 发起一个自定义协议的 URL 请求
- 2.WebView 将请求交给 Native 层处理
- 3.Native 判断该 URL 是否为约定协议
- 4.若是,则拦截并执行对应原生逻辑
- 5.阻止 WebView 继续加载该 URL
在这个过程中:
- 页面并不会真正发生跳转
- URL 只是一个通信信号
本质上看: URL Scheme 拦截并没有建立真正的通信通道,
- 它只是“借用”了浏览器的加载机制。
URL Scheme 的完整格式
scheme://method?query
1.scheme:协议名
2.method:方法名
3.query 参数是传给 Native 的数据
一个标准示例
myapp://openCamera?quality=80&source=album
-
1.
myapp→ 自定义 Scheme(Native侧注册) -
2.
openCamera→ 要调用的原生方法 -
3.
quality=80&source=album→ 参数
URL 在这里只是一个结构化的消息载体,不是为了跳转页面
JS 侧实现示例
//这里定义一个callNative函数 接收方法名 和参数
function callNative(method, params = {}) {
//进行拼接
const query = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&')
//发起URL请求
location.href = `myapp://${method}?${query}`
}
//使用示例
callNative('openCamera', {
quality: 80,
source: 'album'
})
JS 不需要知道 Native 怎么实现 ,它只负责 发信号
Native 侧示例
这里以Android为例
(前端的同学不需要关心,这里是客户端该干的)
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
// 1. 判断是否是自定义 Scheme
if ("myapp".equals(uri.getScheme())) {
// 2. 解析方法名
String method = uri.getHost(); // openCamera
// 3. 解析参数
String quality = uri.getQueryParameter("quality");
String source = uri.getQueryParameter("source");
// 4. 执行原生逻辑
if ("openCamera".equals(method)) {
openCamera(quality, source);
}
// 5. 拦截,不继续加载
return true;
}
return false;
}
});
URL Scheme缺点
1.URL Scheme是单向通信
- 如果你注意我们之前写的JS代码
//使用示例
callNative('openCamera', {
quality: 80,
source: 'album'
})
你会发现,我们的函数没有接收result,也没有callback
因为这本质上是一次 页面加载行为。
- 1.JS 发起后立即结束
- 2.没有返回值
- 3.没有回调通道
JS 无法知道 Native 是否执行成功,也无法获取执行结果。
如果想要回传结果,常见的解决方案是Native 再反向执行一段 JS
(这里就不花时间介绍了,因为这个也不是现在的主流方案)
2.参数维护困难
URL 的设计初衷并不是用来传输复杂数据
如果这个方法传递的参数过长/ 涉及到数组和对象 等复杂数据类型
- URL的表达非常混乱:
myapp://method?data=%7B%22a%22%3A1%2C%22b%22%3A...
3.无法支持复杂业务
就单拿异步回调任务来说,URL Scheme根本解决不了
2.prompt / alert 拦截
- 其实这个也是一个过时的方案,当初它也只是作为过度方案
原理是:
- JS中调用prompt / alert这些弹窗UI,其实它们不属于JS
或者说
- 在 WebView 中,
alert / confirm / prompt这些弹窗 API 并不由 JavaScript 引擎自身实现, 而是 WebView 内核 转发给 Native 层来完成 UI 展示
链路是这样的:
JS Engine
↓
WebView 内核
↓
Native UI 系统
JS 调用 prompt,本质是在请求 Native 弹窗能力。
在 WebView 中:
- 每一次
alert / prompt - 都会触发一个 Native 层回调
- 并且 等待 Native 返回结果
这一步非常关键:
JS 线程会被阻塞,直到 Native 响应。
这就意味着:
- 1.Native 可以在“中途”拦截
- 2.不展示真实弹窗
- 3.改为执行“通信逻辑”
而且prompt本身非常适合作为通信的载体
prompt 有返回值
1.prompt 有输入参数
prompt(message, defaultValue)
这两个参数可以被“借来”:
message→ 通道标识 methoddefaultValue→ 通信数据 params
2.prompt 有返回值
const result = prompt(...)
完美解决了之前JS拿不到result的痛点
3.prompt 是同步的
JS 会 停在这一行:
const result = prompt(...)
直到 Native 调用结束。
这使得:
JS → Native → JS 形成一次“同步调用链”。
(所以其实它还是解决不了异步的问题)
Native侧的拦截逻辑
JS 调用 prompt
↓
WebView 通知 Native:要弹窗
↓
Native 拦截该事件
↓
Native 解析参数
↓
Native 执行原生逻辑
↓
Native 返回字符串结果
↓
WebView 将结果交给 JS
JS侧实现示例
//封装函数 接收method 和 params
function callNative(method, params = {}) {
const message = JSON.stringify({
method,
params
})
// prompt 的返回值由 Native 决定
const result = prompt('JSBridge', message)
//深拷贝result
try {
return JSON.parse(result)
} catch (e) {
return result
}
}
//使用演示
//接收同步结果
const res = callNative('openCamera', {
quality: 80,
source: 'album'
})
console.log(res)
Native 侧实现示例
Andorid (前端同学不用懂,你知道客户端处理了就行)
//设置 WebChromeClient
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(
WebView view,
String url,
String message,
String defaultValue,
JsPromptResult result
) {
// 只拦截我们约定的协议
if ("JSBridge".equals(defaultValue)) {
handleJsBridge(message, result);
return true; // 表示已处理,不弹窗
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
//解析参数 执行逻辑 返回结果
private void handleJsBridge(String message, JsPromptResult result) {
try {
JSONObject json = new JSONObject(message);
String method = json.getString("method");
JSONObject params = json.getJSONObject("params");
if ("openCamera".equals(method)) {
// 执行原生逻辑
openCamera(params);
// 返回结果给 JS(同步)
JSONObject response = new JSONObject();
response.put("success", true);
result.confirm(response.toString());
} else {
result.confirm("");
}
} catch (Exception e) {
result.confirm("");
}
}
prompt方案的缺点
1.prompt是同步API,会阻塞线程
JS使用prompt的时候
prompt('JSBridge', message)
-
1.JS 线程会 立刻被阻塞
-
2.必须等 Native 调用
result.confirm()才能继续执行
这会导致
-
1.页面渲染暂停
-
2.动画卡顿
-
3.复杂调用链极易产生“假死”体验
用户的体验非常差
2.无法支撑复杂通信模型
真实业务中,JSBridge 通常需要支持:
- 1.异步回调
- 2.并发调用
- 3.Promise 化
- 4.事件通知(Native → JS)
- 5.消息队列
3.Webview注入对象
也叫API注入,是目前市面上最主流的解决方案,非常的强大
原理
Native 主动向 WebView 注入一个全局对象,供 JS 直接调用。
Native WebView
│ │
│ 注入对象 │
└───────────────▶ window.NativeBridge
│
└─ JS 直接调用原生方法
这个对象身上包含了所有业务需要的功能函数
通信流程
- 整体流程可以概括为:
1.Native 创建一个 Bridge 对象
2.将该对象注入到 WebView 的 JS 上下文
3.JS 通过 window.xxx 调用原生方法
4.Native 执行对应逻辑
5.JS通过回调/promise拿到结果
JS侧示例
//全局对象 方法
window.NativeBridge.openCamera({
//参数
quality: 80,
source: 'album',
//回调函数
success(res) {
console.log('success', res)
},
fail(err) {
console.error(err)
}
})
Native侧
//创建类
public class NativeBridge {
@JavascriptInterface
public void openCamera(String params) {
// 解析参数
// 执行原生逻辑
}
}
//注入到JS
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");
注意
由于 Android WebView 的限制:
- 1.只支持基本类型
- 2.通常使用 JSON 字符串 作为参数载体
window.NativeBridge.openCamera(JSON.stringify({
quality: 80,
source: 'album'
}))
优点和风险
优点
1.JS侧调用简单
window.NativeBridge.openCamera(params)
2.性能好,不阻塞JS线程
-
不触发页面跳转
-
不阻塞 JS 线程
-
不涉及系统 UI
缺点
1.安全风险
Android 历史漏洞,在 Android 4.2 之前addJavascriptInterface
存在严重漏洞:
- JS 可反射调用任意 Java 方法
- 甚至执行系统命令
- 属于高危远程代码执行漏洞
2.不利于异步与 Promise 化
JS 调用 → Native 执行 → JS 回调
1.回调需要 Native 主动拼接 JS
2.JS 需要提前注册全局函数
3.多个并发调用难以区分
4.消息队列 + 回调机制
当业务进入复杂阶段后,
简单的“方法注入”已经无法满足安全性、异步化和扩展性的要求。
于是,现代 JSBridge 逐渐演进为一种更通用、更稳健的通信模型:
- 基于消息队列的异步通信 + 回调机制
这也是目前主流 App(如支付宝、微信、小程序 WebView 等)所采用的设计思想。
原理
JS 与 Native 不再直接互相调用方法, 而是通过“消息”进行通信
JS Native
│ │
│ 发送消息 │
│────────────────────▶│
│ │
│ 回调结果 │
│◀────────────────────│
通信的最小单位是 Message(消息) ,而不是函数调用。
消息队列能够满足
-
1.多次并发调用
-
2.异步返回
-
3.Promise 化
-
4.Native 主动通知 JS
-
5.错误隔离
-
6.调用顺序保证
整体通信流程
1.JS 发起调用,生成唯一 callbackId
const callbackId = 'cb_' + Date.now() + '_' + Math.random()
2.JS 组装消息对象并入队
const message = {
type: 'call',
method: 'openCamera',
params: {
quality: 80,
source: 'album'
},
callbackId //唯一id
}
JS 会将该消息:
- 1.存入 发送队列
- 2.记录对应的回调函数
3.JS 通知 Native「有新消息了」
常见方式:
- 1.URL Scheme(只作为信号)
- 2.
postMessage - 3.
evaluateJavascript
4.Native 拉取消息队列并处理
Native 主动:
拉取 JS 消息队列
↓
逐条解析
↓
执行对应原生能力
5.Native 回传执行结果
Native 执行完成后:
- 组装回调消息
- 带上原始
callbackId - 发送给 JS
6.JS 根据 callbackId 执行回调
callbacks[callbackId](result) //执行回调函数
delete callbacks[callbackId] //从队列中删除
JS侧实现示例
//消息队列
const messageQueue = []
//回调函数
const callbacks = {}
//发起请求 方法 参数
function callNative(method, params) {
//promise化
return new Promise((resolve, reject) => {
//生成唯一id
const callbackId = 'cb_' + Date.now() + '_' + Math.random()
//放入回调函数
callbacks[callbackId] = resolve
//推入消息队列
messageQueue.push({
method,
params,
callbackId
})
//通知原生
notifyNative()
})
}
//通知原生的函数
function notifyNative() {
location.href = 'jsbridge://message'
}
//调用示例
window.handleNativeCallback = function (callbackId, result) {
callbacks[callbackId]?.(result)
delete callbacks[callbackId]
}
Native侧你无需关心
Native Call JS
本质上 Native Call JS 只有两类策略:
- 1.直接执行 JS(基于全局对象 / 函数)
- 2.基于消息机制的事件 / 回调分发(消息队列)
1.直接执行 JS(基于全局对象 / 函数)
原理:
它和 JS Call Native 中的「WebView 注入全局 API」是同一套思想
JS Call Native 的全局注入方案:
- Native 在 WebView 初始化阶段
- 向 JS 的
window上 注入一个全局对象或方法 - JS 通过
window.xxx()主动调用 Native 能力
// JS Call Native
window.NativeBridge.openCamera()
Native Call JS 的“直接执行 JS”方案:
- JS 在页面加载时
- 主动在
window上暴露全局函数 - Native 通过 WebView 的执行能力
- 直接执行一段 JS 代码,调用这个全局函数
// Native Call JS
window.onNativeMessage(data)
JS 侧示例
// JS 侧:暴露给 Native 调用的全局函数
window.onNativeMessage = function (data) {
console.log('收到来自 Native 的消息:', data)
}
// 可挂多个全局函数
window.onPayResult = function (result) {
if (result.success) {
console.log('支付成功,订单号:', result.orderId)
} else {
console.log('支付失败')
}
}
注意:Native 只能调用挂在 window 上的函数,其他闭包或模块内部函数不可见。
Native 侧示例
Android(WebView)
// 构建要执行的 JS 字符串
String jsCode = "window.onNativeMessage(" +
"{ type: 'login', userId: 123 }" +
");";
// 执行 JS
webView.evaluateJavascript(jsCode, null);
Native 并不直接调用 JS 函数,它只是把 JS 代码交给 WebView 执行
缺点
1.Native无法拿到JS的执行结果
Native直接执行 JS 的方式,本质上是“单向通知” ,不是双向通信
常见的解决方案是
方案 A:JS 再主动调用 Native 回调
window.onNativeMessage = function (data) {
console.log('收到消息', data)
// JS 执行完毕后,通知 Native
if (window.NativeBridge && window.NativeBridge.callback) {
//执行native提供的回调
window.NativeBridge.callback(data)
}
}
-
1.Native 在 JS Call Native 时注入了一个回调对象
-
2.JS 执行完毕后,再主动触发 Native 回调
-
3.本质还是 两次跨环境调用
2.消息队列
原理:
1.Native 不直接找 JS 函数,只负责 发一条“消息” 给 JS
2.JS 端有一个统一的接收器(事件分发表 )去处理消息
3.消息里可以带一个 callbackId,JS 执行完后把结果通过同一通道发回 Native
4.这样就形成了一个“消息往返通道” ,Native 和 JS 都不需要知道对方内部实现
流程图
Native
│
│ sendMessage({event:'loginSuccess', data:{userId:123}, callbackId:'cb_1'})
▼
┌───────────────────┐
│ WebView / JS │
│ __handleNativeMessage │
└───────────────────┘
│
│ 分发事件到 JS 侧监听器
▼
JS 事件处理器 / 回调表
│
│ 执行 JS 逻辑
│
│ (执行完毕后)发送回调结果
▼
Native 收到回调
JS 侧实现示例
// 全局事件分发 + 回调表
const eventListeners = {}
const callbackMap = {}
// 注册事件监听
function on(event, handler) {
eventListeners[event] = eventListeners[event] || []
eventListeners[event].push(handler)
}
// Native 消息入口
window.__handleNativeMessage = function(message) {
//提取参数
const { type, event, data, callbackId } = message
//遍历事件分发表
if (type === 'event') {
(eventListeners[event] || []).forEach(fn => fn(data))
}
//执行回调函数
if (type === 'callback' && callbackId) {
callbackMap[callbackId]?.(data)
delete callbackMap[callbackId]
}
}
// JS 调用 Native 并提供回调(双向通信)
function callNativeWithCallback(method, params = {}, callback) {
//唯一id
const callbackId = 'cb_' + Date.now() + '_' + Math.random()
//放入回调队列
if (callback) callbackMap[callbackId] = callback
const message = { method, params, callbackId }
//传递信息
window.NativeBridge?.postMessage(JSON.stringify(message))
}
Native 侧实现示例(Android)
// Native 向 JS 发送事件消息
public void sendEvent(WebView webView) {
String message = "{"
+ "\"type\":\"event\","
+ "\"event\":\"loginSuccess\","
+ "\"data\":{\"userId\":123},"
+ "\"callbackId\":\"cb_1\""
+ "}";
webView.evaluateJavascript("window.__handleNativeMessage(" + message + ")", null);
}
// Native 发送回调消息给 JS
public void sendCallback(WebView webView, String callbackId) {
String message = "{"
+ "\"type\":\"callback\","
+ "\"callbackId\":\"" + callbackId + "\","
+ "\"data\":{\"success\":true}"
+ "}";
webView.evaluateJavascript("window.__handleNativeMessage(" + message + ")", null);
}
使用示例
// 事件监听
on('loginSuccess', data => {
console.log('收到事件', data)
})
// 调用 Native 并接收回调
callNativeWithCallback('pay', { orderId: 123 }, result => {
console.log('支付结果', result)
})
从上面的示例,我们可以看到 在 JS 端,我们为 Native 消息提供了一个统一入口 window.__handleNativeMessage。它接收到消息后,会根据消息类型进行不同处理:
// Native 消息入口
window.__handleNativeMessage = function(message) {
//提取参数 const { type, event, data, callbackId } = message
//1.事件event类型
if (type === 'event') {
(eventListeners[event] || []).forEach(fn => fn(data))
}
//2.回调callback
if (type === 'callback' && callbackId) {
callbackMap[callbackId]?.(data)
delete callbackMap[callbackId]
}
}
1.事件event
message.type === 'event',Native侧主要发送过来的事件消息 (Native call JS)
JS 会在事件分发表 eventListeners 中查找对应的监听器,并依次调用它们,将数据传递过去。
if (type === 'event') {
(eventListeners[event] || []).forEach(fn => fn(data))
}
2.回调callBack
message.type === 'callback' 并且带有 callbackId,这是JS之前调用Native时,Native处理完成之后返回的结果。 (JS call Native)
JS 会根据 callbackId 在 callbackMap 中找到对应的回调函数,执行它,并在执行完后删除(一次性回调)。
通过这个统一的消息入口,JS 既可以处理 Native 主动发送的事件,也能接收针对 JS 请求的回调结果,实现了双向通信。
完整的方案示例
演示完整的JSbridge双向通信封装
WebView全局注入API+消息队列+回调函数
// jsBridge.js - 企业级 WebView 封装
(function () {
if (window.JSBridge) return;
// ------------------- 内部队列 -------------------
const eventListeners = {}; // 事件监听器,多播
const callbackMap = {}; // 回调队列,一次性
// ------------------- 注册事件 -------------------
function on(event, handler) {
eventListeners[event] = eventListeners[event] || [];
eventListeners[event].push(handler);
}
// ------------------- JS 调用 Native -------------------
function callNative(method, params = {}, callback) {
const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
if (callback) callbackMap[callbackId] = callback;
const message = { method, params, callbackId };
try {
// 调用 Native 全局注入的 postMessage
window.NativeBridge?.postMessage(JSON.stringify(message));
} catch (err) {
console.error('[JSBridge] callNative error', err);
}
}
// ------------------- Native 调用 JS 消息入口 -------------------
function _handleMessage(message) {
try {
const { type, event, data, callbackId } = message;
// 事件广播
if (type === 'event' && event) {
(eventListeners[event] || []).forEach(fn => fn(data));
}
// 回调响应
if (type === 'callback' && callbackId && callbackMap[callbackId]) {
callbackMap[callbackId](data);
delete callbackMap[callbackId];
}
} catch (err) {
console.error('[JSBridge] handleMessage error', err);
}
}
// ------------------- 暴露给全局 -------------------
window.JSBridge = {
on,
callNative,
_handleMessage,
};
})();
使用演示
1.JS 主动调用 Native 并拿回结果
JSBridge.callNative('getUserInfo', { id: 123 }, (data) => {
console.log('Native 返回用户信息:', data);
});
流程如下:
-
1.JSBridge.callNative → 调用封装好的方法
-
'getUserInfo'→ 你希望 Native 执行的功能名 -
{ id: 123 }→ 参数对象 -
(data) => { ... }→ 回调函数,等 Native 执行完后返回结果时调用 -
2.进入 callNative 方法
function callNative(method, params = {}, callback) {
const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
这里把回调函数放入对应的队列 (key是callbackId)
if (callback) callbackMap[callbackId] = callback;
const message = { method, params, callbackId };
...
//把消息发送给Native
window.NativeBridge?.postMessage(JSON.stringify(message));
}
-
3.Native 执行完方法后
-
Native 执行
getUserInfo后,会通过 WebView 调用 JS:
window.JSBridge._handleMessage({
type: 'callback',
callbackId: 'cb_1678901234567_abcd1',
data: { name: '张三', id: 123 }
});
-
type: 'callback'→ 告诉 JS 这是回调消息 -
callbackId→ 对应之前生成的唯一 ID -
data→ Native 执行结果 -
4.JS 处理回调
-
进入JS的
_handleMessage方法
function _handleMessage(message) {
const { type, event, data, callbackId } = message;
在对应callbackMap里面通过callbackId这个key拿到要执行的回调函数
if (type === 'callback' && callbackId && callbackMap[callbackId]) {
callbackMap[callbackId](data); // 执行回调
delete callbackMap[callbackId]; // 删除,保证一次性
}
}
- 5.最终效果
JSBridge.callNative('getUserInfo', { id: 123 }, (data) => {
console.log('Native 返回用户信息:', data);
});
整个流程走完后,控制台会输出:Native 返回用户信息: { name: '张三', id: 123 }
2.Native 主动通知 JS
(事件广播)
// 假设 Native 主动触发网络状态变化
JSBridge._handleMessage({
type: 'event',
event: 'networkChange',
data: { status: 'online' }
});
// JS 注册监听
JSBridge.on('networkChange', (data) => {
console.log('网络状态变化:', data.status);
});
使用示例
JSBridge.on('networkChange', (data) => {
console.log('网络状态变化:', data.status);
});
- 1.调用
JSBridge.on
function on(event, handler) {
eventListeners[event] = eventListeners[event] || [];
eventListeners[event].push(handler);
}
-
event = 'networkChange' -
handler = (data) => { ... } -
JSBridge 内部会变成
eventListeners = {
networkChange: [
function (data) {
console.log('网络状态变化:', data.status);
}
]
};
只是告诉 JSBridge:
“如果以后有人发
networkChange事件,请执行这个函数。”
-
2.Native广播事件
-
Native 调用封装好了的
_handleMessag
window.JSBridge._handleMessage({
type: 'event',
event: 'networkChange',
data: { status: 'online' }
});
- 3.进入JS的统一入口
_handleMessag
function _handleMessage(message) {
const { type, event, data } = message;
if (type === 'event' && event) {
(eventListeners[event] || []).forEach(fn => fn(data));
}
}
① 判断消息类型
type === 'event'
② 找到对应事件名
event === 'networkChange'
也就是eventListeners['networkChange']
③ 触发所有监听器
(eventListeners[event] || []).forEach(fn => fn(data));
在实际的开发中,我们还会移除监听事件
防止重复注册监听、内存泄漏
目前我们的实现的监听是
JSBridge.on('networkChange', handler)
其实是
eventListeners[event].push(handler)
我们再增加一个off监听的方法在封装的类里面
其实就是把这个回调函数在监听事件的回调数组里面删除
function off(event, handler) {
const listeners = eventListeners[event];
if (!listeners) return;
const index = listeners.indexOf(handler);
if (index > -1) {
listeners.splice(index, 1);
}
// 可选:空数组直接清掉
if (listeners.length === 0) {
delete eventListeners[event];
}
}
然后统一暴露出去
window.JSBridge = {
on,
off,
callNative,
_handleMessage,
};
至于callback为什么不用封装off
那是因为我们调用完成之后就直接delete了
callbackMap[callbackId](data)
delete callbackMap[callbackId]
总结
本文介绍了 JSBridge 的概念、原理及实现方式,包括 JS Call Native 和 Native Call JS 的常见策略,帮助读者理解在混合开发中,JS 与原生如何通过桥梁实现高效双向通信。