之前領導要求就打通webView、小程序和iframs三者之間的通訊做一份技術文檔說明,功能是做出來了,結果后面沒有接到那個項目,也就沒有繼續開發下去,但也值得記錄一下。
項目目標
實現webView和小程序通訊,webView和iframe通訊,而后以webView作為中轉站,實現webView中的iframe和小程序通訊。
需求確定
在商城小程序中以webView的形式引入客服聊天頁面IM,在IM中以iframe的形式嵌入第三方頁面,內容和樣式第三方自定義。iframe的操作需求整理如下:
-
跳轉到文章詳情
-
鏈接到另一個客服
-
跳轉到商品詳情
-
跳轉到商品購物車頁
-
跳轉到訂單詳情
-
發送消息
可以整理為三大類:1.小程序頁面跳轉2.向某一個客服發起會話3.在當前會話發送消息
實現
-
webView和小程序通訊
官方文檔對於web-view組件的描述是:承載網頁的容器。會自動鋪滿整個小程序頁面,個人類型的小程序暫不支持使用。所以在小程序中webView是不能以彈窗的形式或者占據頁面一部分和其它內容共存,這個特點微信小程序和支付寶小程序都具備。
webView組件和小程序只能通過bindmessage事件綁定進行通訊,對於這個事件官方的描述是這樣的:網頁向小程序 postMessage 時,會在特定時機(小程序后退、組件銷毀、分享)觸發並收到消息。也就是說無論webView通過postMessage向小程序發送多少次消息,只要不是在特定的時機(小程序后退、組件銷毀、分享),小程序就不會收到webView的消息。
通過以上兩點,我們可以確定小程序的當前頁面只有一個webView是有效內容,那小程序和webView之間的通訊最常用的也就是小程序進行頁面的跳轉了。
// 在webView頁面中引入微信JS文件 <script src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> // 進行頁面跳轉 wx.miniProgram.navigateTo({ url: '/pages/home2/index', // 小程序頁面路徑 })
兩者通過postMessage進行通訊,主要代碼如下
window.addEventListener('message', (event) => { if (window.parent !== event.source) { return } console.log(event, 'event'); //接收到的信息 top.postMessage("發送給父級頁面的信息,可以為對象", '父級頁面url或者*'); }, false);
完整主要代碼
-
小程序
<WebView src="xxxx"></WebView>
-
webView客服聊天頁面IM
// 在webView頁面中引入微信JS文件 <script src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script> let ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == "micromessenger") { //ios的ua中無miniProgram,但都有MicroMessenger(表示是微信瀏覽器) wx.miniProgram.getEnv((res) => { if (res.miniprogram) { console.log("在小程序里"); // 接收到消息 window.addEventListener('message', this.fnTest, false); } else { console.log("不在小程序里"); } }) } else { console.log('不在微信里'); } fnTest = (event) => { let data = event.data; if (data.operation === 'msg') { // 在當前會話發送消息 let r = Math.ceil(Math.random() * 100000) let uuid = this.state.memberData.memberId + Date.now() + r; let sendData = ''; // 0視頻,1圖片,2文件,4文本,8富文本,103iframe switch (data.data.type) { // 消息類型處理 case 103: sendData = data.data.iframeUrl; break; } // this.sendMessage IM中發送消息函數 this.sendMessage(sendData, data.data.type, uuid); } else if (data.operation === 'action') { // 小程序頁面跳轉 wx.miniProgram.navigateTo({ url: data.data.url //'/pages/home2/index' }) } else if (data.operation === 'chat') { let code = StringUtils.getQueryString('code') let url = `index.html?code=${code}&otherId=7&sessionScope=1&userCode=${data.data.userCode}` // 向某一個客服發起會話 window.app.router.push(url) } }
3. 第三方iframe頁面
<!-- 簡單的模擬html頁面 --> <span class="btn" style="margin-left: 0">發送消息</span> <span class="action" style="background: lightpink">跳轉頁面</span> <span class="chat" style="background: lightseagreen">跟他聊聊</span>
window.addEventListener('message', (event) => { if (window.parent !== event.source) { return } console.log(event, 'event'); //接收到的信息 top.postMessage("發送給父級頁面的信息,可以為對象", '父級頁面url或者*'); }, false); // operation 操作類型string msg:發送消息 ,action:跳轉頁面,chat:發起聊天 // type 消息類型string operation為msg時,0視頻,1圖片,2文件,4文本,8富文本,103iframe // 其他具體參數另定 $('.btn').on('click', function () { top.postMessage({ operation: 'msg', data: { data: '消息內容', type: 103, //Number類型 size: 110, //Number類型 name: '', //文件名稱 iframeUrl: 'https://dev.iservice.dtyunxi.cn/i-service/dev/iservice-customer-web-pc/test.html' }, }, '*') }); $('.action').on('click', function () { top.postMessage({ operation: 'action', data: { url: '/pages/home2/index?xxx=xx', // 需要跳轉頁面 } }, '*') }); $('.chat').on('click', function () { top.postMessage({ operation: 'chat', data: { userCode: '007', // 客服工號 } }, '*') });
參數名 | 類型 | 是否必填 | 描述 |
---|---|---|---|
operation | string | 是 | 枚舉值:1. msg:發送消息 2.action:跳轉頁面 3.chat:發起聊天 |
data | object | 是 |
data參數
參數名 | 類型 | 是否必填 | 描述 |
---|---|---|---|
type | number | operation為msg時必填 | 0視頻,1圖片,2文件,4文本,8富文本,103iframe |
size | number | type為2時必填 | 文件大小,默認為0 |
name | string | type為2時必填 | 文件名稱,默認為空 |
iframeUrl | string | type為103時必填 | iframe路徑 |
data | string | operation為msg且type不為103時必填 | type為0,1,2時,為視頻、圖片、文件的路徑,type為4,8時,為發送的內容 |
url | string | operation為action時必填 | 小程序跳轉路徑 |
userCode | string | operation為chat時必填 | 客服工號 |
支付寶小程序
小程序代碼
// .axml <view class="page"> <web-view src="xxxx" onMessage="onmessage"></web-view> </view> // .js Page({ onmessage(e){ // 拿到webView數據,進行一系列事件處理 console.log(e,'e') } });
// 引入js <script type="text/javascript" src="https://appx/web-view.min.js"></script> <!-- 如該 H5 頁面需要同時在非支付寶客戶端內使用,為避免該請求404,可參考以下寫法 --> <!-- 請盡量在 html 頭部執行以下腳本 --> <script> if (navigator.userAgent.indexOf('AlipayClient') > -1) { document.writeln('<script src="https://appx/web-view.min.js"' + '>' + '<' + '/' + 'script>'); } </script> componentWillUnmount() { // 離開頁面,清除監聽 window.removeEventListener('message', this.fnTest, false); } fnTest = (event) => { // alert(JSON.stringify(event), 'event') let data = event.data; if (data.operation === 'msg') { // 在當前會話發送消息 let r = Math.ceil(Math.random() * 100000) let uuid = this.state.memberData.memberId + Date.now() + r; let sendData = ''; // 0視頻,1圖片,2文件,4文本,8富文本,103iframe switch (data.data.type) { // 消息類型處理 case 103: sendData = data.data.iframeUrl; break; } // this.sendMessage IM中發送消息函數 this.sendMessage(sendData, data.data.type, uuid); } else if (data.operation === 'action') { // 小程序頁面跳轉 // my.navigateTo({ url: `../../${data.data.url}` }); my.navigateTo({ url: `../test/test` }); } else if (data.operation === 'chat') { let code = StringUtils.getQueryString('code') let url = `index.html?code=${code}&otherId=7&sessionScope=1&userCode=${data.data.userCode}` // 向某一個客服發起會話 window.app.router.push(url) } } componentDidMount() { window.addEventListener('message', this.fnTest, false); }
特別說明:webView向支付寶小程序發送消息時,並沒有微信小程序接收webView消息的限制【需要在特定時機(小程序后退、組件銷毀、分享)觸發才能收到消息】,只要webView通過 my.postMessage(obj)
向小程序發送消息,小程序就能接收到消息,支付寶小程序和webView的通訊更方便靈活。
流程圖