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方法名

*/

相关推荐
IT_陈寒39 分钟前
Java线程池用错参数,我的服务居然悄悄崩溃了
前端·人工智能·后端
神秘的猪头1 小时前
React 脱围机制(二):重新理解 useEffect——它不是生命周期,而是同步外部系统
react.js
卡布鲁1 小时前
React useMemo 与 useCallback 完全指南:原理、场景与避坑
前端·react.js
碳基修炼1 小时前
排查记:本地 devServer 是 http,浏览器却把 302 重定向升级成了 https
前端·http
步行cgn1 小时前
Spring p 命名空间注入详解
java·前端·spring
何何____1 小时前
Vue 生命周期详解
前端·javascript
江米小枣tonylua2 小时前
TypeScript 全面の拥抱 !Prisma 8 + Electron 升级实战
前端
闪耀之光M782 小时前
npm命令解析
前端
咸鱼老弟2 小时前
AI Agent 的"自主性悖论"——为什么给它的自由度越大,越要配一套更硬的护栏
前端·人工智能
默_笙3 小时前
💫 闭包是个背包:拆解小米前端面试题里的三道"闭包陷阱"
前端·javascript·面试