@docoi/jsbridge-sdk
v2.0.2
Published
轻量级、高性能的 JSBridge SDK,用于 Web 与原生 App 之间的通信
Readme
JSBridge SDK
轻量级、高性能的 JSBridge SDK,用于 Web 与原生 App 之间的通信。
特性
- 🚀 高性能:轻量级设计,无外部依赖,体积小巧
- 🔄 双向通信:支持 H5 调用原生方法,也支持原生调用 H5 方法
- 📱 多平台支持:同时支持 iOS (WKWebView) 和 Android (JavaScriptInterface)
- 🔄 队列管理:内置队列机制,确保顺序调用,避免并发问题
- 📊 调试友好:内置日志系统,支持不同级别的调试输出
- 🔌 事件系统:提供订阅/发布模式的事件系统,方便原生向 H5 推送消息
- 🔄 Promise 支持:支持 Promise 方式调用,代码更简洁
- 🧩 TypeScript:使用 TypeScript 编写,提供完整类型定义
- 🛡️ 框架兼容:适用于各种前端框架 (Vue, React, Angular 等)
安装
npm install jsbridge-sdk --save或者使用 CDN 直接引入:
<script src="https://unpkg.com/jsbridge-sdk/dist/jsbridge.min.js"></script>基本用法
JSBridge 通信层不内建业务缓存;如需复用接口结果,请在业务层按场景实现缓存策略。
初始化
import JSBridge from 'jsbridge-sdk';
// 初始化 JSBridge
JSBridge.init({
debug: true, // 是否开启调试模式
timeout: false // 默认不启用超时
});timeout 默认不启用。只有显式配置全局 timeout 或单次调用的 options.timeout 时,通信层才会在超时后返回 BridgeError。大多数方法默认并发发送;只有单次调用传入 queue: 'serial' 的方法会按同一 method 维度串行执行。
调用原生方法
// 方式 1:回调方式
JSBridge.call('getDeviceInfo', function(result) {
console.log('设备信息:', result);
});
// 方式 2:带参数
JSBridge.call('getLocation', { type: 'gcj02' }, function(result) {
console.log('位置信息:', result);
});
// 方式 3:Promise 方式
try {
const rawResult = await JSBridge.callAsync('getNetworkStatus');
// 业务 SDK 或业务代码在这里判断 rawResult 的业务语义
console.log('Native 原始返回:', rawResult);
// 少数需要互斥执行的 Native 方法,可以在单次调用时指定串行队列
const mediaResult = await JSBridge.callAsync('chooseMedia', {}, {
queue: 'serial',
timeout: 60000
});
console.log('选择媒体结果:', mediaResult);
} catch (error) {
console.error('通信层失败:', error);
}callAsync 只在通信层失败时 reject,例如 Native bridge 不存在、发送异常、参数无法序列化或显式 timeout。只要 Native 正常回调,通信层都会 resolve 原始数据,不会根据 code、success、error 等业务字段判断成功失败。
注册 H5 方法供原生调用
// 注册方法
JSBridge.register('updateUI', function(data) {
console.log('收到原生传来的数据:', data);
// 更新界面逻辑
return {
success: true,
message: '界面已更新'
};
});
// 注销方法
JSBridge.unregister('updateUI');事件监听
// 监听事件
function networkChangeHandler(data) {
console.log('网络状态变化:', data);
}
JSBridge.on('networkChange', networkChangeHandler);
// 取消监听
JSBridge.off('networkChange', networkChangeHandler);一次性事件监听
function loginStateHandler(data) {
console.log('只处理第一次登录态变化:', data);
}
JSBridge.once('loginStateChanged', loginStateHandler);
// 如果事件触发前不再需要监听,也可以用原始 handler 移除
JSBridge.off('loginStateChanged', loginStateHandler);once 注册的监听器会在首次触发前先自动移除,再执行 handler,因此可以避免同步重入导致重复触发。
Bridge 诊断
if (JSBridge.isReady()) {
console.log('Native bridge 可发送');
}
console.log(JSBridge.getBridgeInfo());
// { ready: true, platform: 'ios', bridgeType: 'webkit' }isReady() 只表示当前 WebView 是否检测到可用 Native bridge 入口,不代表某个业务 method 一定存在。
通信层错误
通信层失败会返回轻量的 BridgeError:
{
type: 'NATIVE_UNAVAILABLE',
message: '当前环境不支持调用原生方法',
method: 'getDeviceInfo'
}通信层错误只表示 JSBridge 通道失败,不表示 Native 业务失败。Native 返回的业务错误会被原样透传,code、msg、result 等业务字段应由上层业务 SDK 处理。
在 Vue 中使用
创建插件
// src/plugins/jsbridge.js
import JSBridge from 'jsbridge-sdk';
export default {
install(app, options = {}) {
// 初始化 JSBridge
JSBridge.init({
debug: options.debug || false,
timeout: options.timeout || 15000
});
// 添加全局属性 $jsbridge
app.config.globalProperties.$jsbridge = JSBridge;
// 提供 JSBridge
app.provide('jsbridge', JSBridge);
}
};
// 导出 JSBridge 单例,方便直接使用
export { JSBridge };在 main.js 中注册插件
import { createApp } from 'vue';
import App from './App.vue';
import JSBridgePlugin from './plugins/jsbridge';
const app = createApp(App);
app.use(JSBridgePlugin, { debug: true });
app.mount('#app');在组件中使用
// 选项式 API
export default {
methods: {
getDeviceInfo() {
this.$jsbridge.call('getDeviceInfo', result => {
this.deviceInfo = result;
});
}
}
};
// 组合式 API
import { inject } from 'vue';
export default {
setup() {
const jsbridge = inject('jsbridge');
const getDeviceInfo = async () => {
try {
const result = await jsbridge.callAsync('getDeviceInfo');
// 处理结果
} catch (err) {
// 处理错误
}
};
return { getDeviceInfo };
}
};原生端实现
本 SDK 提供 JavaScript 端的通信实现,原生端需要实现对应通道才能完成通信。新版通道统一使用 mysJSBridge 命名。
通信边界
jsbridge-sdk 只负责全量传递数据,不组装业务响应结构。业务响应应由上层封装层处理,例如 H5 业务方法需要返回:
{
code: 0,
msg: 'ok',
result: {}
}则应由 mys-sdk 或业务封装方法返回这个对象,jsbridge-sdk 会原样传给 Native,不再额外包装 success、data 等字段。
Native 回调 H5
H5 调 Native 后,Native 通过 mysJSBridgeCallback 把本次调用结果回传给 H5。
window.mysJSBridgeCallback(callbackId, data)Native 发事件给 H5
Native 主动广播事件时,通过 mysJSBridgeEvent 通知 H5。事件没有返回值,也不依赖 callbackId。
window.mysJSBridgeEvent('networkChange', {
type: 'wifi'
})Native 主动调用 H5
Native 通过 mysJSBridgeInvoke 主动调用 H5 已注册的方法。
同步方法会直接返回 H5 handler 的原始返回值序列化字符串:
const result = window.mysJSBridgeInvoke('getPageInfo', {})异步方法必须传 callbackId,mysJSBridgeInvoke 会立即返回 null,Promise 完成后再由 H5 通过 Native 回调通道把原始数据传回 Native。
window.mysJSBridgeInvoke('selectAddress', { scene: 'order' }, 'native_callback_001')如果 H5 handler 返回 Promise 但 Native 没有传 callbackId,SDK 会同步返回通信层错误,提示异步 H5 方法必须传 callbackId。
iOS 实现(WKWebView)
// 简化示例
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? [String: Any],
let methodName = body["method"] as? String,
let callbackId = body["callbackId"] as? String else {
return
}
let params = body["params"] as? [String: Any] ?? [:]
handleJSBridgeCall(methodName: methodName, params: params, callbackId: callbackId)
}
func handleJSBridgeCall(methodName: String, params: [String: Any], callbackId: String) {
// Native 返回什么,H5 就会收到什么;业务结构由上层 SDK 约定
let result: [String: Any] = [
"code": 0,
"msg": "ok",
"result": ["platform": "iOS", "version": UIDevice.current.systemVersion]
]
let script = "window.mysJSBridgeCallback('\(callbackId)', \(toJSONString(result)))"
webView.evaluateJavaScript(script, completionHandler: nil)
}
// Native 同步调用 H5 方法
func callH5Method(name: String, data: [String: Any], completion: ((Any?) -> Void)? = nil) {
let script = "window.mysJSBridgeInvoke('\(name)', \(toJSONString(data)))"
webView.evaluateJavaScript(script) { (result, error) in
completion?(result)
}
}
// Native 异步调用 H5 方法
func callAsyncH5Method(name: String, data: [String: Any], callbackId: String) {
let script = "window.mysJSBridgeInvoke('\(name)', \(toJSONString(data)), '\(callbackId)')"
webView.evaluateJavaScript(script, completionHandler: nil)
}
// 触发 H5 事件
func triggerH5Event(eventName: String, data: [String: Any]) {
let script = "window.mysJSBridgeEvent('\(eventName)', \(toJSONString(data)))"
webView.evaluateJavaScript(script, completionHandler: nil)
}
}Android 实现(JavaScriptInterface)
// 简化示例
public class MysJSBridgeInterface {
private WebView webView;
private Context context;
public MysJSBridgeInterface(WebView webView, Context context) {
this.webView = webView;
this.context = context;
}
@JavascriptInterface
public void call(String message) {
try {
JSONObject jsonObject = new JSONObject(message);
String method = jsonObject.getString("method");
JSONObject params = jsonObject.optJSONObject("params");
String callbackId = jsonObject.optString("callbackId");
JSONObject result = handleMethod(method, params);
callH5Callback(callbackId, result);
} catch (JSONException e) {
e.printStackTrace();
}
}
private JSONObject handleMethod(String method, JSONObject params) throws JSONException {
// Native 返回什么,H5 就会收到什么;业务结构由上层 SDK 约定
JSONObject result = new JSONObject();
JSONObject data = new JSONObject();
data.put("platform", "Android");
data.put("version", Build.VERSION.RELEASE);
result.put("code", 0);
result.put("msg", "ok");
result.put("result", data);
return result;
}
// 在主线程中回调 H5
private void callH5Callback(final String callbackId, final JSONObject result) {
if (callbackId == null || callbackId.length() == 0) {
return;
}
webView.post(() -> {
webView.evaluateJavascript(
"window.mysJSBridgeCallback('" + callbackId + "', " + result.toString() + ")",
null
);
});
}
// Native 同步调用 H5 方法
public void callH5Method(String name, JSONObject data, ValueCallback<String> callback) {
webView.post(() -> {
webView.evaluateJavascript(
"window.mysJSBridgeInvoke('" + name + "', " + data.toString() + ")",
callback
);
});
}
// Native 异步调用 H5 方法
public void callAsyncH5Method(String name, JSONObject data, String callbackId) {
webView.post(() -> {
webView.evaluateJavascript(
"window.mysJSBridgeInvoke('" + name + "', " + data.toString() + ", '" + callbackId + "')",
null
);
});
}
// 在主线程中触发 H5 事件
public void triggerH5Event(String eventName, JSONObject data) {
webView.post(() -> {
webView.evaluateJavascript(
"window.mysJSBridgeEvent('" + eventName + "', " + data.toString() + ")",
null
);
});
}
@JavascriptInterface
public void onMysJSBridgeReady() {
// Bridge 已就绪
Log.d("MysJSBridge", "Bridge is ready");
}
}示例
查看 examples 目录中的完整示例:
examples/basic.html- 基本用法示例examples/vue-demo/- Vue 集成示例
API 文档
JSBridge
方法
init(options?: JSBridgeOptions): JSBridgeInterface- 初始化 JSBridgecall(method: string, params?: any, callback?: CallbackFunction, options?: CallOptions): JSBridgeInterface- 调用原生方法callAsync(method: string, params?: any, options?: CallOptions): Promise<any>- Promise 风格的原生方法调用;仅通信层失败时 rejectregister(name: string, handler: MethodHandler): JSBridgeInterface- 注册 H5 方法供原生调用unregister(name: string): JSBridgeInterface- 注销 H5 方法on(eventName: string, handler: EventHandler): JSBridgeInterface- 监听原生事件once(eventName: string, handler: EventHandler): JSBridgeInterface- 一次性监听原生事件off(eventName: string, handler?: EventHandler): JSBridgeInterface- 取消监听原生事件isReady(): boolean- 当前是否检测到可用 Native bridgegetBridgeInfo(): BridgeInfo- 获取 Bridge 诊断信息setDebug(enabled: boolean, options?: Partial<DebugOptions>): JSBridgeInterface- 设置调试模式
类型定义
interface JSBridgeOptions {
debug?: boolean;
timeout?: number | false;
}
type QueueMode = 'parallel' | 'serial';
interface CallOptions {
queue?: QueueMode;
timeout?: number | false;
}
type BridgeErrorType =
| 'NOT_INITIALIZED'
| 'NATIVE_UNAVAILABLE'
| 'SEND_FAILED'
| 'SERIALIZE_FAILED'
| 'TIMEOUT'
| 'INVALID_METHOD';
interface BridgeError {
type: BridgeErrorType;
message: string;
method?: string;
callbackId?: string;
detail?: unknown;
}
interface BridgeInfo {
ready: boolean;
platform: 'ios' | 'android' | 'unknown';
bridgeType: 'webkit' | 'javascriptInterface' | 'none';
version?: string;
capabilities?: string[];
}
type CallbackFunction = (data: any) => void;
type MethodHandler = (data: any) => any | Promise<any>;
type EventHandler = (data: any) => void;
interface DebugOptions {
enabled: boolean;
logLevel?: string;
useConsole?: boolean;
}构建与开发
# 安装依赖
npm install
# 开发模式
npm run dev
# 构建
npm run build
# 运行示例
npm run example:basic