Hybrid app本地開發(fā)如何調(diào)用JSBridge

前天同事問我公司內(nèi)部的小程序怎么對接的,我回憶了一下,簡單記錄了一下前端同學(xué)需要注意的點(diǎn)。

背后還有小程序架構(gòu)、網(wǎng)絡(luò)策略等等。當(dāng)時(shí)恰逢小程序架構(gòu)調(diào)整,(老架構(gòu)的時(shí)候我就發(fā)現(xiàn)了有一個(gè)問題點(diǎn)可以優(yōu)化,但是跟那邊人反饋之后,人家表示不要我管??,新架構(gòu)時(shí)發(fā)現(xiàn)這個(gè)問題還巧妙的遺留下來了??)我雖然不負(fù)責(zé)那塊,但是本著這樣不優(yōu)雅的原則,還是跟新架構(gòu)的對接人講了我的優(yōu)化方案,講明白了之后,同時(shí)上報(bào)各自直系領(lǐng)導(dǎo),并建議我領(lǐng)導(dǎo)牽頭開會推動。最后,無奈存量數(shù)據(jù)太多,老架構(gòu)那邊權(quán)衡之后決定不改動。(多說了幾句,權(quán)當(dāng)記錄一下)

1、背景

公司研發(fā)的一款服務(wù)軟件App(姑且稱為“大地”),提供了包涵消息、待辦、工作臺、同事圈和通訊錄五大功能模塊,其中,工作臺里集成了包括公司的移動客戶端、PC端以及第三方平臺的部分功能/服務(wù)(統(tǒng)稱為“應(yīng)用”)。

我今天要講的是這個(gè)集成平臺以什么方式展現(xiàn)“應(yīng)用”,答案是:借鑒了微信的架構(gòu),自研了“小程序”接入“應(yīng)用”。

我司小程序具有一種相對開放能力(面向全公司),賦能業(yè)務(wù)快速數(shù)字化、場景敏捷迭代,并且可在“大地”上便捷的獲取和使用,同時(shí)具有完善的使用體驗(yàn)(這就是嚴(yán)格的接入審核標(biāo)準(zhǔn)帶來的好處)。

在“大地”開發(fā)者平臺,創(chuàng)建小程序會自動創(chuàng)建配套的公眾號(公眾號是為了推送消息使用,可訂閱)。小程序開發(fā)不限制技術(shù)選型,開發(fā)完成之后按照小程序接入規(guī)范打包上架小程序,審核發(fā)布。

簡單來說,可以把“大地”看成是一個(gè)“釘釘”,我現(xiàn)在要把我們的業(yè)務(wù)功能投放到“大地”上,就需要接入“大地”小程序,以小程序的方式在“大地”上為用戶提供服務(wù)。

小程序架構(gòu):Cordova框架做的WebView,運(yùn)行我開發(fā)的前端程序,通過Nginx幫我把請求代理到微服務(wù)網(wǎng)關(guān),由網(wǎng)關(guān)轉(zhuǎn)發(fā)到目的主機(jī)處理請求。它雖然看上去是一個(gè)Native App,但只有一個(gè)UI WebView,里面訪問的是一個(gè)Web App,對我來說就是開發(fā)一個(gè)H5應(yīng)用調(diào)用一些所需的JSBridge,也就是所謂的Hybrid App

下面看一下本地開發(fā)中的一些問題,以及我是怎么處理的

2、問題

Hybrid App本地開發(fā)過程中沒有真實(shí)的Native環(huán)境的,同樣也無法使用JSBridge,這就會帶來一個(gè)問題:跟原生交互的行為只能發(fā)布小程序才可以調(diào)試,本地玩不了,這...,相當(dāng)fuck。

目的是想讓本地開發(fā)同小程序測試環(huán)境具有相同的體驗(yàn),我的想法是在本地模擬JSBridge的方法,盡管不能帶來真實(shí)的效果,至少觸發(fā)了某個(gè)行為之后要有個(gè)反應(yīng),不至于讓操作流程看起來像是“脫節(jié)”的(實(shí)際跟原生的交互行為并不多,比如:拍照、彈窗提示、定位等等)。

因此,我要做的就是本地模擬JSBridge的一些方法,開發(fā)時(shí)觸發(fā)了這些原生交互行為之后提示一些信息,等到上架小程序測試環(huán)境時(shí),在手機(jī)上會用真實(shí)的JSBridge方法自動替換掉我模擬實(shí)現(xiàn)的方法。

于是我就開始了下面的準(zhǔn)備工作。

  1. 搞清楚JSBridge運(yùn)行的原理

  2. 本地模擬JSBridge的方法

  3. 上架小程序是自動使用真實(shí)的JSBridge

3、了解JSBridge

JSBridge:望文生義就是jsNative之前的橋梁,而實(shí)際上JSBridge確實(shí)是JSNative之前的一種通信方式。

簡單的說,JSBridge就是定義NativeJS的通信,Native只通過一個(gè)固定的橋?qū)ο笳{(diào)用JS,JS也只通過固定的橋?qū)ο笳{(diào)用NativeJSBridge另一個(gè)叫法及大家熟知的Hybrid app技術(shù)。

了解即可,更多的請參考

下圖展示了JSBridge的工作流程??

上圖中左側(cè)部分正式我要做的,具體請看下文

看累了,三連一下,回看不迷路喲??

3.1、我們的JSBridge

推測“大地”那邊的JSBridge應(yīng)該是自己寫的,沒有初始化JSBridge的操作

當(dāng)調(diào)用JSBridge時(shí),必須在頁面完全加載完成之后才能夠拿到全局的JSBridgeCordova框架提供deviceready事件,該事件觸發(fā)的時(shí)候表示全局的JSBridge掛載成功。(注意:這就是我接下來操作的切入點(diǎn),嘻嘻)

簡單寫下如下:

document.addEventListener('deviceready', function () {
  console.log('deviceready OK!');
  JSAPI.showToast(0, '提示信息')
}, false)

需要注意的是,在開發(fā)環(huán)境,是沒有 deviceready 事件的,所以上面的代碼并不會執(zhí)行,只有在app里面運(yùn)行的時(shí)候才會執(zhí)行。

思考:

JSBridge必須是在deviceready事件觸發(fā)后方能使用的,因此首先要做的就是自定義deviceready事件,本地環(huán)境可以在load事件里觸發(fā)自定義deviceready事件,生產(chǎn)環(huán)境下監(jiān)聽deviceready事件即可

4、JS發(fā)起自定義事件

我是用 CustomEvent 構(gòu)造函數(shù),繼承至 Event,文檔看這里

  1. 用法
new CustomEvent(eventName, params);
  1. 示例

創(chuàng)建一個(gè)自定義事件

const event=new CustomEvent('mock-event'); 
  1. 傳遞參數(shù)

這里值得注意,需要把想要傳遞的參數(shù)包裹在一個(gè)包含detail屬性的對象,否則傳遞的參數(shù)不會被掛載

function createEvent(params, eventName = 'mock-event') {
    return new CustomEvent(eventName, { detail: params });
}

const event = createEvent({ id: '0010' });
  1. 發(fā)起事件

調(diào)用dispatchEvent方法發(fā)起事件,傳入你剛才創(chuàng)建的方法

window.dispatchEvent(event); 
  1. 監(jiān)聽事件
window.addEventListener('mock-event', ({ detail: { id } }) => {
    console.log('id',id) // 會在控制臺打印0010
});
  1. 示例:
document.body.addEventListener('show', (event) => { console.log(event.detail); });
// 觸發(fā)
let myEvent = new CustomEvent('show', {
    detail: {
        username: 'xixi',
        userid: '2022'
    }
});
document.body.dispatchEvent(myEvent);

了解了自定義事件之后,通過自定義事件模擬觸發(fā)deviceready事件,這樣上面的 deviceready 事件監(jiān)聽就可以執(zhí)行了。

注意:這里還要確定一個(gè)問題,在什么時(shí)候觸發(fā)自定義事件deviceready呢?

5、確定 deviceready 事件執(zhí)行時(shí)機(jī)

  • 只需要編寫如下代碼,查看輸出結(jié)果即可
window.addEventListener('load', function () {
  console.log('load OK!');
}, false);
 
document.addEventListener('deviceready', function () {
  console.log('deviceready OK!');
}, false);
  • 結(jié)果輸出
load OK!
deviceready OK!

由此可知,執(zhí)行順序:load --> deviceready

6、自定義事件模擬Cordova deviceready事件

  1. 自定義deviceready事件

  2. 根據(jù)上面測試執(zhí)行順序得出的結(jié)論,我在load事件里觸發(fā)自定義事件

  3. 在開發(fā)環(huán)境下模擬一些用到的JSBridge-API,比如下面寫到的 JSAPI.showToast() 方法

  • mockEvent.js
if (process.env.NODE_ENV === 'development') {
  //  自定義事件
  let myEvent = new CustomEvent('deviceready');
  
  // 模擬JSAPI事件
  window['JSAPI'] = {
    showToast(type, desc) {
      console.log(type, desc);
    }
  }
  
  // ...

  // 開發(fā)環(huán)境下,在 原生 load 方法之后 觸發(fā)自定義事件
  window.addEventListener('load', function () {
    console.log('load OK!');
    setTimeout(() => {
      document.body.dispatchEvent(myEvent);
    }, 100)
  }, false);
}

7、封裝deviceReady方法

實(shí)現(xiàn)在Cordova框架觸發(fā)deviceready事件的時(shí)候感知到,以便于在deviceReady事件觸發(fā)后執(zhí)行JS-API

可用于開發(fā)環(huán)境和非開發(fā)環(huán)境

7.1、方式一

這里采用鏈?zhǔn)秸{(diào)用的方式,

以下這種借助 Promise 的實(shí)現(xiàn),在這種場景下其實(shí)是不合理的??
只是形式上類似,其實(shí)并不是

  1. 定義
  • mixin.js
deviceReady() {
  return new Promise((resolve) => {
    window.addEventListener('deviceready', function () {
      resolve("ready go!");
    }, false);
  })
}
  1. 組件內(nèi)使用JS-API

使用JSAPI可以如下這么寫

this.deviceReady().then((res) => {
  console.log(res); // ready go!
  JSAPI.showToast(0, '提示')
})


this.deviceReady().then((res) => {
  JSAPI.getUserInfo((res) => {
    console.log(res);
  }, (err) => {
    console.log(err);
  });
})
  1. 開發(fā)環(huán)境執(zhí)行效果如下

7.2、方式二(推薦)

改寫成通用的事件監(jiān)聽函數(shù),支持鏈?zhǔn)秸{(diào)用

  • 開發(fā)環(huán)境下,由mockEvent.js文件里的dispatchEvent觸發(fā)自定義的deviceready事件;

  • 小程序里運(yùn)行,則由真實(shí)的deviceready事件觸發(fā)

  1. 定義
  • mixin.js
receiver(type) {
  let callbacks = {
      fns: [],
      then: function(cb){
          this.fns.push(cb);
          return this;
      }
  };
  
  document.addEventListener(type, function(ev) {
    let fns = callbacks.fns.slice();
    for(let i = 0, l = fns.length; i < l; i++){
        fns[i].call(this, ev);
    }
  });

  return callbacks;
}
  1. 使用
this.receiver('deviceready').then((ev) => { 
  console.log(ev);
  JSAPI.getUserInfo(
    (res) => {
      console.log(res);
    },
    (err) => {
      console.log(err);
    }
  )
})
this.receiver("click")
  .then(() => console.log("hi"))
  .then(()=> console.log(22));

最后

當(dāng)應(yīng)用發(fā)布到app上,就是監(jiān)聽的真實(shí)的 Cordova框架的 deviceready 事件了,之后也就可以拿到真實(shí)的JSAPI了,以上只是為了在開發(fā)環(huán)境的時(shí)候模擬使用JSAPI。防止在開發(fā)環(huán)境下直接調(diào)用JSAPI飄紅的情況,當(dāng)然也是可以加try catch處理的,只不過個(gè)人感覺模擬事件使得代碼看起來更加優(yōu)雅別致一點(diǎn),使用更加絲滑,酌情食用??。

軟件架構(gòu)非常有意思,感興趣的可以交流探索,嘻嘻。


我是 甜點(diǎn)cc

熱愛前端,也喜歡專研各種跟本職工作關(guān)系不大的技術(shù),技術(shù)、產(chǎn)品興趣廣泛且濃厚,等待著一個(gè)創(chuàng)業(yè)機(jī)會。主要致力于分享實(shí)用技術(shù)干貨,希望可以給一小部分人一些微小幫助。

我排斥“新人迷茫,老人看戲”的現(xiàn)象,希望能和大家一起努力破局。營造一個(gè)良好的技術(shù)氛圍,為了個(gè)人、為了我國的數(shù)字化轉(zhuǎn)型、互聯(lián)網(wǎng)物聯(lián)網(wǎng)技術(shù)、數(shù)字經(jīng)濟(jì)發(fā)展做一點(diǎn)點(diǎn)貢獻(xiàn)。數(shù)風(fēng)流人物還看中國、看今朝、看你我

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容