JSBridge 原理

理解 JSBridge 的命名由来和实现方式

问题

为什么叫 “JS” Bridge?JSBridge 是如何实现 Web 与 Native 通信的?

解答

为什么是 “JS” Bridge

因为在 Hybrid App 中,JavaScript 是唯一能在 WebView 中运行并与 Native 交互的语言

  • WebView 本质是一个浏览器内核,只能执行 JS
  • Native(iOS/Android)无法直接操作网页 DOM
  • 所以必须通过 JS 作为”桥梁”来传递消息

简单说:JS 是 Web 端唯一的出口,所有通信都要经过它

通信原理

JSBridge 的通信是双向的:

┌─────────────┐                    ┌─────────────┐
│   WebView   │  ←── JSBridge ──→  │   Native    │
│    (JS)     │                    │ (iOS/Android)│
└─────────────┘                    └─────────────┘

JS 调用 Native

方式一:注入 API(推荐)

Native 向 WebView 的 window 对象注入方法:

// Native 注入后,JS 可以直接调用
window.NativeBridge.showToast('Hello')
window.NativeBridge.getLocation((data) => {
  console.log(data.lat, data.lng)
})

方式二:URL Scheme 拦截

JS 通过修改 URL,Native 拦截并解析:

// JS 端发起调用
function callNative(method, params, callback) {
  const callbackId = `cb_${Date.now()}`
  // 存储回调函数
  window[callbackId] = callback
  
  // 构造 scheme URL
  const url = `myapp://${method}?params=${encodeURIComponent(JSON.stringify(params))}&callback=${callbackId}`
  
  // 通过 iframe 发起请求(不会引起页面跳转)
  const iframe = document.createElement('iframe')
  iframe.style.display = 'none'
  iframe.src = url
  document.body.appendChild(iframe)
  setTimeout(() => iframe.remove(), 100)
}

// 使用
callNative('share', { title: '分享标题' }, (result) => {
  console.log('分享结果:', result)
})

Native 调用 JS

Native 直接执行 WebView 中的 JS 代码:

// JS 端:暴露全局方法供 Native 调用
window.onNativeEvent = function(eventName, data) {
  console.log('收到 Native 事件:', eventName, data)
  // 处理事件...
}

// Native 端(伪代码):
// iOS: webView.evaluateJavaScript("onNativeEvent('login', {userId: 123})")
// Android: webView.evaluateJavascript("onNativeEvent('login', {userId: 123})", null)

完整的 JSBridge 封装

const JSBridge = {
  callbacks: {},
  callbackId: 0,
  
  // JS 调用 Native
  call(method, params = {}) {
    return new Promise((resolve) => {
      const id = ++this.callbackId
      this.callbacks[id] = resolve
      
      // 如果 Native 注入了方法,直接调用
      if (window.NativeBridge && window.NativeBridge[method]) {
        window.NativeBridge[method](JSON.stringify({ ...params, callbackId: id }))
      } else {
        // 降级使用 URL Scheme
        const iframe = document.createElement('iframe')
        iframe.src = `myapp://${method}?data=${encodeURIComponent(JSON.stringify({ ...params, callbackId: id }))}`
        iframe.style.display = 'none'
        document.body.appendChild(iframe)
        setTimeout(() => iframe.remove(), 100)
      }
    })
  },
  
  // 供 Native 回调
  onCallback(id, result) {
    const callback = this.callbacks[id]
    if (callback) {
      callback(result)
      delete this.callbacks[id]
    }
  },
  
  // 供 Native 主动调用
  onEvent(eventName, data) {
    const event = new CustomEvent(`native:${eventName}`, { detail: data })
    window.dispatchEvent(event)
  }
}

// 暴露给 Native
window._jsBridge = JSBridge

// 使用示例
async function share() {
  const result = await JSBridge.call('share', { title: '标题', url: location.href })
  console.log('分享结果:', result)
}

// 监听 Native 事件
window.addEventListener('native:login', (e) => {
  console.log('用户登录:', e.detail)
})

关键点

  • JS 是桥梁:WebView 只能运行 JS,所以 Web 与 Native 的通信必须经过 JS
  • JS → Native:注入 API(直接调用)或 URL Scheme 拦截(iframe/location)
  • Native → JS:通过 evaluateJavaScript 执行 JS 代码
  • 回调机制:通过 callbackId 匹配请求和响应,实现异步回调
  • 双向通信:JSBridge 本质是一套约定好的消息传递协议