之前领导要求就打通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的通讯更方便灵活。
流程图