学习js bridge
概念
首先需要知道Hybrid
开发,即Native
和Web
混合开发的一种技术。
优缺点对比
特性 | web | native |
---|---|---|
灵活性 | 无需发版,灵活 | 需要发版,不灵活 |
功能 | 不能使用 native 接口 |
可以使用所有 native 接口 |
兼容性 | ios 和 android 使用一套代码 |
需要开发两套代码 |
混合开发的意义就是结合两者的优点,摒弃缺点。需要快速迭代的需求可以使用前端进行开发,已经稳定的功能可以使用客户端进行开发
js bridge
是实现客户端和前端数据通信的技术
封装
功能
主要功能
- 前端同步调用客户端方法
- 前端异步调用客户端方法,需要传
callback
- 客户端同步调用前端方法,对前端来说相当于事件
辅助功能
- 判断当前环境是浏览器还是App,浏览器环境无App方法。不能通过
userAgent
进行判断,浏览器自带的手机模拟器的userAgent
值也是native
。判断前端是否存在客户端注入的方法是可行的 - 是否开启 debug模式,会在控制台打印调用客户端方法的细节
- 判断客户端是否存在某个方法
- 实现
EventEmitter
用来管理事件
实现
前端同步调用客户端
最简单的方式就是客户端把方法fn
注册到window
对象,然后前端直接调用window.fn()
去触发。
以上方法会存在一些问题
- 污染全局变量,如果想在
window
对象上添加同名函数,会覆盖客户端方法 - 不好进行管理
比较好的方法是将客户端注入的方法统一放到window
对象的属性中,例如window.nativeApi = {}
,
前端调用客户端某个方法window.nativeApi.foo()
前端异步调用客户端
异步调用其实是由**两次单向(前端先调用客户端,然后客户端在调用前端)**调用组成
前端是无法直接将异步回调当作参数传给客户端并让其执行的,异步回调只能在前端执行
目前可行的做法是在前端创建一个map
用来存储前端的callback
,key
为callbackId
,value
为callback
。前端调用客户端方法时将callbackId
作为参数传过去,当客户端方法执行结束后需要执行前端回调时,调用前端的指定方法handleMessage
,将这个callbackId
和callback 参数
传回前端。前端在根据callbackId
去map
中找到指定的callback
,执行并将参数传给这个callback
,这样就实现了异步调用客户端方法了
客户端同步调用前端
基本原理就是前端将方法注入到window
或者window
对象下的属性中,客户端在去调用这个方法。
基于上述原理可以有以下两种方式实现客户端调用前端方法
第一种就是上述原理所述方法,但是这种方法会有弊端。相同方法名的函数只能注册一次,多次注册后者会覆盖前者,但是在开发中经常会遇到需要多次注册相同函数的需求。例如wakeUp
事件,在页面唤醒后执行前端函数,往往很多页面都会注册wakeUp
事件,这时就需要有多个wakeUp
函数。第二种方法可以满足这种需求
1 | // 在a页面注册wakeUp函数 |
第二种方法是基于事件驱动的,重点在前端。我们可以把客户端调用前端方法理解成前端注册了一个事件,等待客户端去触发这个事件相当于前端监听客户端的这个事件。因为前端注册方法后并不会立即执行,而是等待触发了客户端某些行为后才会触发,例如wakeUp
页面唤醒事件
既然是事件,肯定可以注册多次,并且需要支持注册事件、触发事件、卸载事件等一系列操作,在这里使用订阅-发布者模式EventEmitter
最为适合,这样也就支持了在多个页面注册相同函数(事件)的功能了
首先开发一个EventEmitter
,基于它封装三个函数。分别是Native_on(type, callback)
监听客户端方法;Native_off(type, callback)
取消监听客户端方法;handleMessage(message)
触发事件并且参数中会携带事件的信息(事件名,回调参数等等),该方法会绑定到window
上由客户端进行触发。这三个方法的本质就是对EventEmitter
中的队列进行维护
1 | class EventEmitter { |
两种方法比较
特性 | 方法一 | 方法二 |
---|---|---|
复杂度 | 不复杂,直接将前端方法注入window 等待客户端调用 |
复杂,客户端只需要调用handleMessage 方法通知前端,大部分工作是有前端完成(EventEmitter) |
功能 | 不支持同一方法注册多个回调,否则会进行覆盖,最终只能保留一个 | 支持同一方法注册多个回调,同时支持卸载回调 |
使用哪种方法取决于是否有注册多个回调的需求,没有则选择方法一非常简单,如果有则必须使用第二种方法
通信时参数的数据结构
双端通信必须提前统一接口数据结构
handleMessage
1 | { |