微信小程序 webview 與 h5 實時通訊的實現

在做 React Native 應用時,如果需要在 App 裏面內嵌 H5 頁面,那麼 H5 與 App 之間可以通過 Webview 的 PostMessage 功能實現實時的通訊,但是在小程序裏面,雖然也提供了一個 webview 組件,但是,在進行 postMessage 通訊時,官方文檔裏面給出了一條很變態的說明:

網頁向小程序 postMessage 時,會在特定時機(小程序後退、組件銷燬、分享)觸發並收到消息。e.detail = { data }data 是多次 postMessage 的參數組成的數組

這裏面已經說的很明白了,不管我們從 H5 頁面裏面 postMessage 多少次,小程序都是收不到的,除非:

  1. 用戶做了回退到上一頁的操作
  2. 組件銷燬
  3. 用戶點擊了分享

這裏面其實我沒有完全說對,官方其實說的是 小程序後退,並沒有說是用戶做回退操作,經過我的實測,確實人家表達得很清楚了,我們通過微信官方的SDK調起的回退也是完全可行的:

wx.miniProgram.navigateBack()

大體思路

從上面的分析和實測中我們可以知道,要實現無需要用戶操作即可完成的通訊,第三種情況我們是完全不需要考慮了的,那麼來仔細考慮第 1 和第 2 種場景。

第 1 種方式:回退

當我們想通過網頁向小程序發送數據,同時還可以回退到上一個頁面時,我們可以在 wx.miniProgram.postMessage 之後,立馬調用一次 wx.miniProgram.navigateBack(),此時小程序的操作是:

  1. 處理 postMessage 信息
  2. 回退到上一頁

我們在處理 postMessage 的時候做一些特殊操作,可以將這些數據保存下來

第 2 種方式:組件銷燬

這是我感覺最合適的一種方式,可以讓小程序拿到數據,同時還保留在當前頁面,只需要銷燬一次 webview 即可,大概的流程就是:

  1. 小程序 postMessage
  2. 小程序 navigateTo 將小程序頁面導向一個特殊的頁面
  3. 小程序的那個特殊頁面立馬回退到 webview 所在的頁面
  4. webview 所在的頁面的 onShow 裏面,做一次處理,將 webview 銷燬,然後再次打開
  5. 觸發 onMessage 拿到數據
  6. H5 頁面再次被打開

這種方式雖然變態,但是至少可以做到實時拿到數據,同時還保留在當前 H5 頁面,唯一需要解決的是,在做這整套操作前,H5 頁面需要做好狀態的緩存,要不然,再次打開之後,H5 的數據就清空了。

第 1 種方式:通過回退,將數據提交給小程序之後傳遞給 webview 的上一頁面

這種方式實現起來其實是很簡單的,我們現在新建兩個頁面:

sandbox/canvas-by-webapp/index.js

const app = getApp();

Page({
  data: {
    url: '',
    dimension: null,
    mime: '',
  },
  handleSaveTap: function() {
    wx.navigateTo({
      url: '/apps/browser/index',
      events: {
        receiveData: data => {
          console.log('receiveData from web browser: ', data);
          if (typeof data === 'object') {
            const { url, mime, dimension } = data;
            if (url && mime && dimension) {
              this.setData({
                url,
                dimension,
                mime,
              });

              this.save(data);
            }
          }
        }
      }
    })
  },

  save: async function({ url, mime, dimension }) {
    try {
      await app.saveImages([url]);
      app.toast('保存成功!');
    } catch (error) {
      console.log(error);
      app.toast(error.message || error);
    }
  },
});

上面的代碼中,核心點,就在於 wx.navigateTo 調用時,裏面的 events 參數,這是用來進行與 /apps/browser/index 頁面通訊,接收數據用的。

apps/browser/index.js

我省略了絕大多數與本文無關的代碼,保存最主要的三個:

Page({
  onLoad() {
    if (this.getOpenerEventChannel) {
      this.eventChannel = this.getOpenerEventChannel();
    }
  },
  handleMessage: function(message) {
    const { action, data } = message;
    if (action === 'postData') {
      if (this.eventChannel) {
        this.eventChannel.emit('receiveData', data);
      }
    }
  },

  handlePostMessage: function(e) {
    const { data } = e.detail;
    if (Array.isArray(data)) {
      const messages = data.map(item => {
        try {
          const object = JSON.parse(item);
          this.handleMessage(object);
          return object;
        } catch (error) {
          return item;
        }
      });

      this.setData({
        messages: [...messages],
      });
    }
  },
})

其實,onLoad 方法中,我們使用了自微信 SDK 2.7.3 版本開始提供的 getOpenerEventChannel 方法,它可以創建一個與上一個頁面的事件通訊通道,這個我們會在 handleMessage 中使用。

handlePostMessage 就是被 bindmessagewebview 上面的方法,它用於處理從 H5 頁面中 postMessage 過來的消息,由於小程序是將多次 postMessage 的消息放在一起發送過來的,所以,與其它的Webview不同點在於,我們拿到的是一個數組: e.detail.datahandlePostMessage 的作用就是遍歷這個數組,取出每一條消息,然後交由 handleMessage 處理。

handleMessage 在拿到 message 對象之後,將 message.actionmessage.data 取出來(*這裏需要注意,這是我們在 H5 裏面的設計的一種數據結構,你完全可以在自己的項目中設計自己的結構),根據 action 作不同的操作,我在這裏面的處理是,當 action === 'postData' 時,就通過 getOpenerEventChannel 得到的消息通道 this.eventChannel 將數據推送給上一級頁面,也就是 /sandbox/canvas-by-webapp,但是不需要自己執行 navigateBack ,因爲這個需要交由 H5 頁面去執行。

H5 頁面的實現

我的 H5 主要就是使用 html2canvas 庫生成 Canvas 圖(沒辦法,自己在小程序裏面畫太麻煩了),但是這個不在本文討論過程中,我們就當是已經生成了 canvas 圖片了,將其轉爲 base64 文本了,然後像下面這樣做:

wx.miniProgram.postMessage({
  data: JSON.stringify({
    action: 'postData',
    data: 'BASE 64 IMAGE STRING'
  })
});

wx.miniProgram.navigateBack()

將數據 postMessage 之後,立即 navigateBack() ,來觸發一次回退,也就觸發了 bindmessage 事件。

使用銷燬 webview 實現實時通訊

接下來,咱就開始本文的重點了,比較變態的方式,但是也沒想到更好的辦法,所以,大家將就着交流吧。

(先睡個覺,感覺這個今天晚上寫不完,雖然原理很簡單)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章