微信小程序webview与h5通过postMessage实现实时通讯的(2)

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 文本了,然后像下面这样做:

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

将数据 postMessage 之后,立即 navigateBack() ,来触发一次回退,也就触发了 bindmessage 事件。

使用销毁 webview 实现实时通讯
接下来,咱就开始本文的重点了,比较变态的方式,但是也没想到更好的办法,所以,大家将就着交流吧。

H5 页面的改变

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

H5 页面只是将 wx.miniProgram.navigateBack() 改成了 wx.miniProgram.navigateTo('/apps/browser/placeholder') ,其它的事情就先都交由小程序处理了。

/apps/browser/placeholder

这个页面的功能其实很简单,当打开它了之后,做一点点小操作,立马回退到上一个页面(就是 webview 所在的页面。

Page({ data: { loading: true }, onLoad(options) { const pages = getCurrentPages(); const webviewPage = pages[pages.length - 2]; webviewPage.setData( { shouldReattachWebview: true }, () => { app.wechat.navigateBack(); } ); }, });

我们一行一行来看:

const pages = getCurrentPages();

这个可以拿到当前整个小程序的页面栈,由于这个页面我们只允许从小程序的 Webview 页面过来,所以,它的上一个页面一定是 webview 所在的页面:

const webviewPage = pages[pages.length - 2];

拿到 webviewPage 这个页面对象之后,调用它的方法 setData 更新一个值:

webviewPage.setData( { shouldReattachWebview: true }, () => { app.wechat.navigateBack(); } );

shouldReattachWebview 这个值为 true 的时候,表示需要重新 attach 一次 webview,这个页面的事件现在已经做完了,回到 webview 所在的页面

apps/browser/index.js 页面

我同样只保留最核心的代码,具体的逻辑,我就直接写进代码里面了。

Page({ data: { shouldReattachWebview: false, // 是否需要重新 attach 一次 webview 组件 webviewReattached: false, // 是否已经 attach 过一次 webview 了 hideWebview: false // 是否隐藏 webview 组件 }, onShow() { // 如果 webview 需要重新 attach if (this.data.shouldReattachWebview) { this.setData( { // 隐藏 webview hideWebview: true, }, () => { this.setData( { // 隐藏之后立马显示它,此时完成一次 webview 的销毁,拿到了 postMessage 中的数据 hideWebview: false, webviewReattached: true, }, () => { // 拿到数据之后,处理 canvasData this.handleCanvasData(); } ); } ); } }, // 当 webview 被销毁时,该方法被触发 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], }); } }, // 处理每一条消息 handleMessage: function(message) { const {action, data} = message // 如果 saveCanvas action if (action === 'saveCanvas') { // 将数据先缓存进 Snap 中 const { canvasData } = this.data; // app.checksum 是我自己封装的方法,计算任何数据的 checksum,我拿它来当作 key // 这可以保证同一条数据只会被处理一次 const snapKey = app.checksum(data); // 只要未处理过的数据,才需要再次数据 if (canvasData[snapKey] !== true) { if (canvasData[snapKey] === undefined) { // 将数据从缓存进 `snap` 中 // 这也是我自己封装的一个方法,可以将数据缓存起来,并且只能被读取一次 app.snap(snapKey, data); // 设置 canvasData 中 snapKey 字段为 `false` canvasData[snapKey] = false; this.setData({ canvasData, }); } } } }, // 当 webview 被重新 attach 之后,canvas 数据已经被保存进 snap 中了, handleCanvasData: async function handleCanvasData() { const { canvasData } = this.data; // 从 canvasData 中拿到所有的 key,并过滤到已经处理过的数据 const keys = Object.keys(canvasData).filter(key => canvasData[key] === false); if (keys.length === 0) { return; } for (let i = 0; i < keys.length; i += 1) { try { const key = keys[i]; const { url } = app.snap(key); // 通过自己封装的方法,将 url(也就是Base64字符)保存至相册 const saved = await app.saveImages(url); // 更新 canvasData 对象 canvasData[key] = true this.setData({ canvasData }) console.log('saved: ', saved); } catch (error) { app.toast(error.message); return; } } }, })

对应的 index.wxml 文件内容如下:

<web-view src="https://www.jb51.net/{{src}}" wx:if="https://www.jb51.net/{{src}}" bindmessage="handlePostMessage" wx:if="{{!hideWebview}}" />

流程回顾与总结

打开 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 状态

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ab297901738c05c3dc08836907027859.html