文章转自:https://www.jb51.net/article/167957.htm
这篇文章主要介绍了微信小程序webview与h5通过postMessage实现实时通讯的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
在做 React Native 应用时,如果需要在 App 里面内嵌 H5 页面,那么 H5 与 App 之间可以通过 Webview 的 PostMessage 功能实现实时的通讯,但是在小程序里面,虽然也提供了一个 webview 组件,但是,在进行 postMessage 通讯时,官方文档里面给出了一条很变态的说明:
网页向小程序 postMessage 时,会在特定时机(小程序后退、组件销毁、分享)触发并收到消息。e.detail = { data },data 是多次 postMessage 的参数组成的数组
这里面已经说的很明白了,不管我们从 H5 页面里面 postMessage 多少次,小程序都是收不到的,除非:
- 用户做了回退到上一页的操作
- 组件销毁
- 用户点击了分享
这里面其实我没有完全说对,官方其实说的是 小程序后退,并没有说是用户做回退操作,经过我的实测,确实人家表达得很清楚了,我们通过微信官方的SDK调起的回退也是完全可行的:
1 |
|
大体思路
从上面的分析和实测中我们可以知道,要实现无需要用户操作即可完成的通讯,第三种情况我们是完全不需要考虑了的,那么来仔细考虑第 1 和第 2 种场景。
第 1 种方式:回退
当我们想通过网页向小程序发送数据,同时还可以回退到上一个页面时,我们可以在 wx.miniProgram.postMessage 之后,立马调用一次 wx.miniProgram.navigateBack(),此时小程序的操作是:
- 处理 postMessage 信息
- 回退到上一页
我们在处理 postMessage 的时候做一些特殊操作,可以将这些数据保存下来
第 2 种方式:组件销毁
这是我感觉最合适的一种方式,可以让小程序拿到数据,同时还保留在当前页面,只需要销毁一次 webview 即可,大概的流程就是:
- 小程序 postMessage
- 小程序 navigateTo 将小程序页面导向一个特殊的页面
- 小程序的那个特殊页面立马回退到 webview 所在的页面
- webview 所在的页面的 onShow 里面,做一次处理,将 webview 销毁,然后再次打开
- 触发 onMessage 拿到数据
- H5 页面再次被打开
这种方式虽然变态,但是至少可以做到实时拿到数据,同时还保留在当前 H5 页面,唯一需要解决的是,在做这整套操作前,H5 页面需要做好状态的缓存,要不然,再次打开之后,H5 的数据就清空了。
第 1 种方式:通过回退,将数据提交给小程序之后传递给 webview 的上一页面
这种方式实现起来其实是很简单的,我们现在新建两个页面:
sandbox/canvas-by-webapp/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
上面的代码中,核心点,就在于 wx.navigateTo 调用时,里面的 events 参数,这是用来进行与 /apps/browser/index 页面通讯,接收数据用的。
apps/browser/index.js
我省略了绝大多数与本文无关的代码,保存最主要的三个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
其实,onLoad 方法中,我们使用了自微信 SDK 2.7.3 版本开始提供的 getOpenerEventChannel 方法,它可以创建一个与上一个页面的事件通讯通道,这个我们会在 handleMessage 中使用。
handlePostMessage 就是被 bindmessage 至 webview 上面的方法,它用于处理从 H5 页面中 postMessage 过来的消息,由于小程序是将多次 postMessage 的消息放在一起发送过来的,所以,与其它的Webview不同点在于,我们拿到的是一个数组: e.detail.data, handlePostMessage 的作用就是遍历这个数组,取出每一条消息,然后交由 handleMessage 处理。
handleMessage 在拿到 message 对象之后,将 message.action 与 message.data 取出来(*这里需要注意,这是我们在 H5 里面的设计的一种数据结构,你完全可以在自己的项目中设计自己的结构),根据 action 作不同的操作,我在这里面的处理是,当 action === 'postData' 时,就通过 getOpenerEventChannel 得到的消息通道 this.eventChannel 将数据推送给上一级页面,也就是 /sandbox/canvas-by-webapp,但是不需要自己执行 navigateBack ,因为这个需要交由 H5 页面去执行。
H5 页面的实现
我的 H5 主要就是使用 html2canvas 库生成 Canvas 图(没办法,自己在小程序里面画太麻烦了),但是这个不在本文讨论过程中,我们就当是已经生成了 canvas 图片了,将其转为 base64 文本了,然后像下面这样做:
1 2 3 4 5 6 7 8 |
|
将数据 postMessage 之后,立即 navigateBack() ,来触发一次回退,也就触发了 bindmessage 事件。
使用销毁 webview 实现实时通讯
接下来,咱就开始本文的重点了,比较变态的方式,但是也没想到更好的办法,所以,大家将就着交流吧。
H5 页面的改变
1 2 3 4 5 6 7 8 |
|
H5 页面只是将 wx.miniProgram.navigateBack() 改成了 wx.miniProgram.navigateTo('/apps/browser/placeholder') ,其它的事情就先都交由小程序处理了。
/apps/browser/placeholder
这个页面的功能其实很简单,当打开它了之后,做一点点小操作,立马回退到上一个页面(就是 webview 所在的页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
我们一行一行来看:
1 |
|
这个可以拿到当前整个小程序的页面栈,由于这个页面我们只允许从小程序的 Webview 页面过来,所以,它的上一个页面一定是 webview 所在的页面:
1 |
|
拿到 webviewPage 这个页面对象之后,调用它的方法 setData 更新一个值:
1 2 3 4 5 6 7 8 |
|
shouldReattachWebview 这个值为 true 的时候,表示需要重新 attach 一次 webview,这个页面的事件现在已经做完了,回到 webview 所在的页面
apps/browser/index.js 页面
我同样只保留最核心的代码,具体的逻辑,我就直接写进代码里面了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
|
对应的 index.wxml 文件内容如下:
1 |
|
流程回顾与总结
- 打开 webview 页面,打开 h5
- h5 页面生成 canvas 图,并转为 base64 字符
- 通过 wx.miniProgram.postMessage 将 base64 发送给小程序
- 调用 wx.miniProgram.navigateTo 将页面导向一个特殊页面
- 在特殊页面中,将 webview 所在页面的 shouldReattachWebview 设置为 true
- 在特殊页面中回退至 webview 所在页面
- webview 所在页面的 onShow 事件被触发
- 在 onShow 事件检测 shouldReattachWebview 是否为 true,若为 true
- 将 hideWebview 设置为 true,引起 web-view 组件的销毁
- handlePostMessage 被触发,解析所有的 message 之后交给 handleMessage 逐条处理
- handleMessage 发现 action === 'saveCanvas' 的事件,拿到 data
- 根据 data 计算 checksum ,以 checksum 为 key 缓存下来数据,并将这个 checksum 保存到 canvasData 对象中
- 此时 hideWebview 被 onShow 里面 setData 的回调中的 setData 重新置为 false,web-view 重新加 attach,H5页面重新加载
- webview 重新 attach 之后, this.handleCanvasData 被触发,
- handleCanvasData 检测是否有需要保存的 canvas 数据,如果有,保存,修改 canvasData 状态
整个流程看旧去很繁琐,但是写起来其实还好,这里面最主要的是需要注意,数据去重,微信的 postMessage 里面拿到的永远 都是 H5 页面从被打开到关闭的所有数据。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。