jsbridge
是随着Hybrid App
的流行而产生的一种技术。那么Hybrid App
是啥?Hybrid App
又称混合App
,即同时使用了前端web技术(js,css,html)和原生native技术(java,kotlin,swfit,object-c)进行开发的移动应用。javascript
如今不少App的页面,不必定都是原生实现的,多是经过webview直接加载一个线上的h5站点。好比打开某粉红App的会员购页面,其实就是个移动端的网站。css
这么一说,好像和混合开发也没啥联系。不过你仔细看下页面的右上角,会发现有个分享按钮:点击分享图标,能够把当前页面分享到第三方平台,分享后,web页面须要知道是否分享成功。html
这里就涉及了native端和web端的通讯:native分享的内容,须要web端的js进行设置(js -> native);native分享成功后,须要把消息通知给js(natvie -> js)。为实现两端的双向通讯机制,就须要jsbridge
技术了。前端
由于h5网页是经过原生端的webview加载的,因此原生端对当前网页拥有很高的权限:Native端能够直接在当前webview里执行js代码。java
// web端 function nativeCallback(data) { console.log('data', data); }
咱们在js的执行环境里定义了一个全局方法nativeCallback
,native端能够直接执行nativeCallback(123)
方法,也就把数据传给了js。web
这种方案是否是有点熟悉,jsonp
就是相似的原理:只不过调用全局方法的时机,从服务器端改为了native端。npm
前端常见的协议有:json
https://www.baidu.com
file:///Users/deepred/myproject/index.html
其实咱们也能够自定义协议:sslocal://openModal?text=hello
,客户端经过分析这段scheme
就能知道web端要调用原生的哪些方法,同时数据也经过query参数进行了传递。小程序
那web端如何发送这段scheme给native端呢?服务器
console
alert
prompt
全局方法。alert('sslocal://openModal?text=hello')
native能够拦截webview中的这些方法,从而调用原生方法。
const ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'sslocal://openModal?text=hello'; document.body.appendChild(ifr);
web端加载了一个iframe,请求了sslocal://openModal?text=hello
, native端经过拦截url请求,从而调用原生方法。
使用scheme
字符串来调用方法始终不够直观,其实咱们还能够向webview里注入一个js全局对象,这个全局对象拥有调用native的方法的能力。
// nativeApp是由native端注入的全局变量 nativeApp.openModal('hello');
前面咱们介绍的几种方法,都只能单向通讯。如何进行双向通讯呢?这时候就须要前端本身实现一个JS-SDK,维护js回调函数的Map。
首先,咱们假设客户端会向webview中注入一个全局对象BILIAPP
// BILIAPP是原生端注入的 const BILIAPP = { invoke(methodName, param, onSuccessKey, onFailKey) {} }
该对象有个invoke
方法,接收4个参数:
咱们无法直接传函数给原生方法,因此这里只能传回调函数的id,id对应的实际函数,由前端这边维护。
sdk.js
let id = 1; const uuid = () => { return `callback_${id++}`; }; // BILISDK是web端注入的 const BILISDK = { // key是回调函数的id // value是回调函数的值 callbacks: { }, // 暴露给前端使用的方法,支持Promise invokeP(methodName, param) { return new Promise((resolve, reject) => { const successCb = (data) => { resolve(data); }; const failureCb = (data) => { reject(data); }; return BILISDK._invoke(methodName, param, successCb, failureCb); }); }, // 实际真正调用原生对象的方法 _invoke(methodName, param, successCb, failureCb) { const onSuccessKey = uuid(); const onFailKey = uuid(); // 存入callbacks hash表中 this.callbacks[onSuccessKey] = successCb; this.callbacks[onFailKey] = failureCb; // BILIAPP是否注入成功 BILIAPP && BILIAPP.invoke && BILIAPP.invoke(methodName, JSON.stringify(param), onSuccessKey, onFailKey); }, // 暴露给原生端使用的方法 invokeFromNative(key, param) { if (typeof param === "string") { try { param = JSON.parse(param) } catch (ex) { } } const callback = this.callbacks[key]; if (callback) { callback(param); } } } // 使用BILISDK调用原生方法 BILISDK.invokeP('getVersion').then((res) => { console.log('res', res); })
如今前端调原生方法,不要直接使用BILIAPP.invoke
,而是经过BILISDK.invokeP
间接调用。BILISDK.invokeP
支持Promise化,同时维护了一个hash表
const BILISDK = { callbacks: { 'callback_1': function() {}, 'callback_2': function() {}, }, }
BILISDK.invokeFromNative
是暴露给Native端使用的。当原生方法调用完成后,根据成功仍是失败,Native端能够调用BILISDK.invokeFromNative(成功或者失败的id)
,而这个id就是当初BILIAPP.invoke
调用时传进来的id。
经过上面的方法,咱们就实现了js -> native -> js 的双向通讯了。固然理论上,咱们还需实现:native -> js -> native 的双向通讯,可是原理是同样的,这时客户端就须要本身实现一个Native-SDK,维护Native端回调函数的Map。
前面咱们实现的sdk.js
,如何引入web站点呢?
把sdk打包成umd规范的js静态文件,上传到cdn或者发布到npm
import
导入便可。该方案,前端维护sdk。(维护成本高)