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。(維護成本高)