Files
youle_app_ios_v2/ylgamehall/Resources/JS/WebViewJavascriptBridge.js
T
joywayer af1a2028e5 Phase 1.8:WebViewJavascriptBridge.js 协议源码入 Bundle
H5 端桥协议实现,与 BridgeBus 的消息格式 1:1 对齐:
H5 → Native 走 window.webkit.messageHandlers.WVJBHandler.postMessage(...),
Native → H5 走 window.WebViewJavascriptBridge._handleMessageFromObjC(base64)。

- 新增 ylgamehall/Resources/JS/WebViewJavascriptBridge.js(~130 行)
  - bridge.registerHandler(name, fn):H5 注册等待 Native callHandler
  - bridge.callHandler(name, data, responseCallback):H5 主动调 Native
  - bridge._handleMessageFromObjC(b64):Native 入口,base64 → JSON parse
    → 派发到 H5 handler 或匹配响应 responseCallbacks
  - base64 解码用 decodeURIComponent + atob 处理 UTF-8 多字节字符
  - 兼容 marcuswestin 旧式 window.WVJBCallbacks 队列握手:注入后
    立即 flush 队列(H5 业务代码无需修改)
- RootViewController 加 Phase 1.8 烟雾测试:Bundle.main.url 找到 JS 文件
  并打印 bytes / head;synchronized group 会把 Resources/JS/ 子目录
  扁平化到 bundle 根,注释里说明此约束
- BuildProject 通过,bundle/WebViewJavascriptBridge.js 实际存在
- 下一步 Phase 1.9 BridgedWebView 用 WKUserScript(.atDocumentStart) 注入
  本文件到所有 H5 页面,桥真正生效
2026-06-22 00:16:55 +08:00

152 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* WebViewJavascriptBridge.js
*
* H5 端桥协议代码,与 Native BridgeBus@MainActor BridgeProtocol 实现)
* 一一对应。详见 docs/H5-Native-Implementation-Design.md §3.2 / §3.3。
*
* 在 BridgedWebView 构造时通过 WKUserScript.atDocumentStart)注入到所有
* 加载的 H5 页面。H5 业务代码运行时,window.WebViewJavascriptBridge 已就绪。
*
* 协议(Native ↔ H5 双向 JSON 消息):
* - H5 → Nativewindow.webkit.messageHandlers.WVJBHandler.postMessage({...})
* - Native → H5window.WebViewJavascriptBridge._handleMessageFromObjC('<base64>')
* - 单条消息:
* { handlerName, data?, callbackId? } // 主动调用对端 handler
* { responseId, responseData? } // 响应对端早前 callHandler 的 callbackId
*/
;(function () {
if (window.WebViewJavascriptBridge) { return; }
// ── 内部状态 ─────────────────────────────────────────────
var messageHandlers = {}; // H5 注册的 handlername → fn(data, responseCallback)
var responseCallbacks = {}; // 等待 Native 响应的 JS 回调:callbackId → fn(responseData)
var nextCallbackId = 1;
// ── 对外 API ─────────────────────────────────────────────
var bridge = window.WebViewJavascriptBridge = {
/**
* H5 注册 handler,等待 Native 主动 callHandler。重名覆盖。
* @param {string} handlerName
* @param {function(data, responseCallback)} handler
*/
registerHandler: function (handlerName, handler) {
messageHandlers[handlerName] = handler;
},
/**
* H5 主动调 Native handler。
* @param {string} handlerName
* @param {*} data 任意 JSON 可序列化值
* @param {function(responseData)} [responseCallback]
*/
callHandler: function (handlerName, data, responseCallback) {
var callbackId = null;
if (typeof responseCallback === 'function') {
callbackId = 'cb_' + (nextCallbackId++) + '_' + Date.now();
responseCallbacks[callbackId] = responseCallback;
}
var message = { handlerName: handlerName };
if (data !== undefined && data !== null) {
message.data = data;
}
if (callbackId) {
message.callbackId = callbackId;
}
_postMessageToNative(message);
},
/**
* Native → H5 入口。BridgeBus.sendToJS 会注入:
* window.WebViewJavascriptBridge._handleMessageFromObjC('<base64of JSON>')
* base64 包装是为了避免 JSON 内容里的单引号 / 反斜杠扰乱 evaluateJavaScript 的字符串字面量。
*/
_handleMessageFromObjC: function (base64String) {
try {
var json = _decodeBase64UTF8(base64String);
var message = JSON.parse(json);
_dispatchFromNative(message);
} catch (e) {
console.error('[WVJB] handle message from native failed:', e);
}
}
};
// ── 内部 ─────────────────────────────────────────────────
function _postMessageToNative(message) {
try {
window.webkit.messageHandlers.WVJBHandler.postMessage(message);
} catch (e) {
console.error('[WVJB] post to native failed (WVJBHandler 通道未注册?):', e);
}
}
function _dispatchFromNative(message) {
// Native 响应 H5 早前 callHandler
if (message.responseId) {
var cb = responseCallbacks[message.responseId];
if (cb) {
delete responseCallbacks[message.responseId];
try { cb(message.responseData); }
catch (e) { console.error('[WVJB] response callback threw:', e); }
}
return;
}
// Native 主动调用 H5 handler
var handler = messageHandlers[message.handlerName];
if (!handler) {
console.warn('[WVJB] no H5 handler registered for:', message.handlerName);
return;
}
var responseCallback;
if (message.callbackId) {
var responseId = message.callbackId;
responseCallback = function (responseData) {
_postMessageToNative({
responseId: responseId,
responseData: responseData
});
};
}
try { handler(message.data, responseCallback); }
catch (e) { console.error('[WVJB] H5 handler "' + message.handlerName + '" threw:', e); }
}
/**
* 标准 base64 → UTF-8 字符串。
* 直接 atob() 只能处理 latin1;这里用 decodeURIComponent + escape 处理多字节字符。
*/
function _decodeBase64UTF8(b64) {
return decodeURIComponent(Array.prototype.map.call(atob(b64), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
// ── marcuswestin 旧式握手兼容 ─────────────────────────────
// 部分 H5 代码这样初始化 bridge:
// function setupWebViewJavascriptBridge(callback) {
// if (window.WebViewJavascriptBridge) return callback(WebViewJavascriptBridge);
// if (window.WVJBCallbacks) return window.WVJBCallbacks.push(callback);
// window.WVJBCallbacks = [callback];
// /* iframe trick - WKWebView 模式不需要 */
// }
// setupWebViewJavascriptBridge(function (bridge) { bridge.registerHandler(...) });
//
// 我们在 documentStart 就注入 bridge,正常路径下 H5 拿到的 window.WebViewJavascriptBridge
// 已存在,会立即同步 callback(bridge)。但若 H5 代码本身用了 WVJBCallbacks 队列模式
// (Array.push 在 bridge 之前),需要在此处 flush 一次。
if (Array.isArray(window.WVJBCallbacks)) {
var pending = window.WVJBCallbacks;
delete window.WVJBCallbacks;
for (var i = 0; i < pending.length; i++) {
try { pending[i](bridge); }
catch (e) { console.error('[WVJB] pending WVJBCallback threw:', e); }
}
}
})();