浏览器原理 · 33/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

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 本质是一套约定好的消息传递协议