jsBridge接入流程

import deviceInfo from './deviceInfo'

import { setRefreshToken } from './token'

// === 设备判断 ===

const u = navigator.userAgent

export const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1

export const isIOS = !!u.match(/\(i\^;+;( U;)? CPU.+Mac OS X/)

export const isNativeMobile = (isAndroid || isIOS) && new URLSearchParams(window.location.search).get('native')

// === 调试信息 ===

console.log('=== 设备检测 ===', {

userAgent: u,

isAndroid,

isIOS,

isNativeMobile,

urlParams: window.location.search

})

// === nativeTokenReady: 外部可 await 等待 token 注入 ===

let nativeTokenReadyResolve: (_token: string) => void

export const nativeTokenReady: Promise<string> = new Promise((resolve) => {

nativeTokenReadyResolve = resolve

})

// === 桥接状态管理 ===

let bridgeInitialized = false

let tokenReceived = false

/**

* 安卓桥函数:需要 bridge.init()

*/

const androidFunction = (callback: any) => {

console.log('=== 安卓桥函数调用 ===', new Date().toISOString())

if (window.WebViewJavascriptBridge) {

console.log('=== 安卓桥已存在,直接回调 ===')

callback(window.WebViewJavascriptBridge)

} else {

console.log('=== 安卓桥不存在,等待WebViewJavascriptBridgeReady事件 ===')

document.addEventListener('WebViewJavascriptBridgeReady', () => {

console.log('=== 收到WebViewJavascriptBridgeReady事件 ===')

callback(window.WebViewJavascriptBridge)

}, false)

}

}

/**

* iOS 桥函数:用 iframe 触发注入

*/

const iosFunction = (callback: any) => {

console.log('=== iOS桥函数调用 ===', new Date().toISOString())

if (window.WebViewJavascriptBridge) {

console.log('=== iOS桥已存在,直接回调 ===')

return callback(window.WebViewJavascriptBridge)

}

if (window.WVJBCallbacks) {

console.log('=== iOS桥回调已存在,添加到队列 ===')

return window.WVJBCallbacks.push(callback)

}

console.log('=== 创建iOS桥回调队列和iframe ===')

window.WVJBCallbacks = callback

const WVJBIframe = document.createElement('iframe')

WVJBIframe.style.display = 'none'

WVJBIframe.src = 'wvjbscheme://BRIDGE_LOADED'

document.documentElement.appendChild(WVJBIframe)

setTimeout(() => {

document.documentElement.removeChild(WVJBIframe)

console.log('=== 移除iOS桥iframe ===')

}, 0)

}

/**

* 处理token注入

*/

const handleTokenInjection = (data: any) => {

console.log('=== 处理token注入 ===', new Date().toISOString())

console.log('原始数据:', data)

console.log('数据类型:', typeof data)

try {

let res = data

if (isAndroid && typeof data === 'string') {

try {

res = JSON.parse(data)

console.log('安卓JSON解析成功:', res)

} catch (e) {

console.error('安卓JSON解析失败:', e, data)

return

}

}

console.log('处理后的数据:', res)

console.log('数据字段:', Object.keys(res || {}))

// 尝试多种可能的token字段

const validToken = res'token'

if (validToken) {

console.log('=== 设置token成功 ===', validToken)

setRefreshToken(validToken)

tokenReceived = true

nativeTokenReadyResolve(validToken)

} else {

console.warn('=== 没有找到有效的token ===')

// 即使没有token也要resolve,避免无限等待

if (!tokenReceived) {

tokenReceived = true

nativeTokenReadyResolve('')

}

}

// 设置设备版本(如果有的话)

if (res?.'Device-Version') {

console.log('=== 设置设备版本 ===', res'Device-Version')

deviceInfo.setdeviceVersion(res'Device-Version')

}

} catch (error) {

console.error('=== 处理token注入失败 ===', error, data)

if (!tokenReceived) {

tokenReceived = true

nativeTokenReadyResolve('')

}

}

}

// === 导出的统一桥接初始化函数 ===

export function setupBridge(): any {

if (bridgeInitialized) {

console.log('=== 桥接已初始化,跳过重复初始化 ===')

return

}

console.log('=== 开始初始化桥接 ===', new Date().toISOString())

bridgeInitialized = true

window.setupWebViewJavascriptBridge = isAndroid ? androidFunction : iosFunction

window.setupWebViewJavascriptBridge((bridge) => {

console.log('=== 桥接回调执行 ===', new Date().toISOString())

console.log('桥接对象:', bridge)

// 注册原生注入 refreshToken 的方法

bridge.registerHandler('injectRefreshToken', handleTokenInjection)

console.log('=== 已注册injectRefreshToken处理器 ===')

// 安卓需要调用 bridge.init()

if (isAndroid) {

console.log('=== 调用安卓bridge.init ===')

bridge.init((_msg: any, responseCallback: any) => {

console.log('=== 安卓bridge.init回调 ===', _msg)

responseCallback('H5 已收到')

})

}

})

}

// === 封装 callHandler 调用 ===

export const bridge = {

callHandler: (methodName: string, params?: any, callback?: any): any => {

console.log('=== 调用桥接方法 ===', methodName, params)

if (window?.setupWebViewJavascriptBridge) {

window.setupWebViewJavascriptBridge((bridge) => {

bridge.callHandler(methodName, params || null, (data: any, fn: any) => {

console.log('=== 桥接方法回调 ===', methodName, data)

callback?.(data, fn)

})

})

} else {

console.warn('=== 桥接未初始化,无法调用方法 ===', methodName)

}

}

}

// === 初始化桥接 ===

if (isNativeMobile) {

console.log('=== 检测到原生环境,开始初始化 ===', new Date().toISOString())

// 立即初始化

setupBridge()

// 监听全局事件(兜底方案)

const messageHandler = (event: any) => {

console.log('=== 收到message事件 ===', new Date().toISOString(), event.data)

// 检查是否是token相关的事件

if (event.data && (event.data.token || event.data.refreshToken || event.data.type === 'injectRefreshToken')) {

console.log('=== 通过message事件收到token ===', event.data)

handleTokenInjection(event.data)

}

}

window.addEventListener('message', messageHandler)

// 延迟初始化(兜底)

setTimeout(() => {

if (!tokenReceived) {

console.log('=== 延迟初始化桥接 ===', new Date().toISOString())

setupBridge()

}

}, 1000)

// 超时处理

setTimeout(() => {

if (!tokenReceived) {

console.warn('=== 10秒内未收到token,可能存在问题 ===')

nativeTokenReadyResolve('')

}

}, 10000)

}

/**

* app携带地址栏参数

* native=true

* theme=light | dark

*

* bridge方法名

*/

相关推荐
夏幻灵2 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
Lvan的前端笔记2 小时前
docker:每个前端项目一个 Nginx 容器还是只有一个Nginx容器
前端·nginx·docker
三天不学习2 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
xcs194053 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js
明月_清风4 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
Csvn5 小时前
框架性能优化
前端
雪芽蓝域zzs7 小时前
第 5 节:Marker 点击弹出 InfoWindow 信息窗口
vue.js
回眸&啤酒鸭7 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户69371750013847 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员7 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端