顺晟科技
2021-06-16 10:49:09
478
在做React Native应用时,如果需要在App中嵌入H5页面,那么可以通过Webview postMessage功能实现H5和App的实时通信。然而,尽管小程序中也提供了webview组件,但是在PostMessage通信期间,官方文档中给出了一个非常异常的描述:
网页向小程序发布消息时,会在特定时间触发并接收消息(小程序撤退,组件销毁,共享)。E.detail={data},数据是多次postMessage的参数组成的数组
已经明确声明,无论我们从H5页面发送多少次消息,小程序都不能被接收,除非:
用户回滚到上一页
组件破坏
用户单击了共享
事实上,我并不完全正确。官方居然说小程序退了,并没有说用户做了回滚操作。经过我的实际测量,人们真的表达得非常清楚。我们通过微信的官方SDK设置了退路。也是完全可行的:
wx.miniProgram.navigateBack()
总体思路
从以上分析和实际测量可以知道,为了实现无需用户操作即可完成的通信,完全不需要考虑第三种情况,所以要仔细考虑种和第二种场景。
种方法:后退
当我们想通过网页向小程序发送数据,同时又可以回到上一页时,可以在wx.miniProgram.postMessage之后立即调用wx.miniProgram.navigateBack()此时小程序的操作是:
处理邮件后信息
返回上一页
我们在处理邮件时会做一些特殊的操作,这样可以保存这些数据
方法2:组件销毁
这是最适合我让小程序获取数据并保持在当前页面的方式。它只需要破坏webview一次。大致过程如下:
邮件后小程序
小程序导航将小程序页面导向一个特殊的页面
小程序的特殊页面会立即退回到webview所在的页面
在网页视图所在的页面上,执行一个过程,销毁网页视图,然后再次打开它
触发消息获取数据
H5页面再次打开
虽然这种方法不正常,但它至少可以实时获取数据,并将其保存在当前的H5页面上。需要解决的是,在做这一整套操作之前,需要对H5页面进行缓存,否则,H5的数据会在再次打开后被清空。
种方式:通过回滚,数据被提交给applet,然后传递到webview的上一页
这种方式实现起来其实很简单。我们现在正在创建两个新页面:
sandbox/canvas-by-web app/index . js
const app=GetApp();
页面({
数据: {
url: ' ',
尺寸:为空,
mime: ' ',
},
handleSaveTap:函数(){
wx.navigateTo({
URL : '/应用程序/浏览器/索引',
事件: {
接收数据:数据={
console . log(' receiveData from web browser : ',data);
if(type of data==' object '){
const { url,mime,dimension }=数据;
if (url mime维度){
this.setData({
url,
维度,
哑剧,
});
this.save(数据);
}
}
}
}
})
},
save:异步函数({ url,mime,dimension }) {
尝试{
wait app . save images([URL]);
App.toast('保存成功!');
} catch(错误){
console.log(错误);
app . toast(error . message | | error);
}
},
});
在上面的代码中,核心点在于wx.navigateTo调用中的events参数,用于与/apps/browser/index页面进行通信,接收数据。
app/browser/index . js
我省略了大部分与本文无关的代码,并保存了三个主要代码:
页面({
onLoad() {
if(this . GetOpenereventChannel){
this . event channel=this . getopenereventschannel();
}
},
handleMessage:函数(消息){
const { action,data }=message
if(action==' PostData '){
if (this.eventChannel) {
this . event channel . emit(' receiveData ',data);
}
}
},
handlePostMessage:函数(e) {
const { data }=e.detail
if (Array.isArray(data)) {
const messages=data.map(item={
尝试{
const object=JSON . parse(item);
this.handleMessage(对象);
返回对象;
} catch(错误){
退货项目;
}
});
this.setData({
消息: [.消息],
});
}
},
})
实际上,在onLoad方法中,我们使用微信SDK 2 . 7 . 3版以来提供的getOpenerEventChannel方法,它可以创建一个与上一页的事件通信通道,我们将在handleMessage中使用。
HandlePostMessage是将消息绑定到webview的方法,用于处理来自H5页面中的PostMessage的消息。由于小程序多次一起发送邮件的消息,与其他网络视图的不同之处在于,我们得到的是一个数组:e . detail . data HandlePostMessage的功能是遍历这个数组,取出每个消息,然后将其交给handleMessage进行处理。
HandleMessage获取消息对象后取出message.action和message.data这里需要注意的是,这是我们在H5设计的数据结构,你可以在自己的项目中设计自己的结构),根据动作做不同的操作。我这里的处理是,当action===' postData '时,数据通过消息通道this.eventChannel推送到上一页,从getOpenerEventChannel获得,也就是/sandbox/canvas-by-webapp,但是你不需要自己执行navigateBack,因为这个需要由H5页面执行。
H5网页的实现
我的H5主要是用html2Canvas库生成Canvas(没办法,小程序里画太麻烦了),但本文不讨论这个,我们就当它是canvas图像,把它变成base64的文本,然后做如下操作:
wx.miniProgram.postMessage({
data: JSON.stringify({
action: 'postData ',
数据: '基本64图像字符串'
})
});
wx.miniProgram.navigateBack()
发布数据后,立即导航回()以触发回滚,这将触发后期消息事件。
使用毁坏的网络视图实现实时通信
接下来就从这篇文章的重点开始吧,比较变态的一种方式,但是我没有想到更好的方式,就交流一下吧。
H5页面更改
wx.miniProgram.postMessage({
data: JSON.stringify({
action: 'postData ',
数据: '基本64图像字符串'
})
});
wx . MiniProgram . navigate to('/apps/browser/placeholder ');
在H5页面,wx.miniProgram.navigateBack()改为wx . miniprogram . navigateto('/apps/browser/placeholder '),其他的都是先由小程序处理。
/应用程序/浏览器/占位符
这个页面的功能其实很简单。打开后做一点操作,马上回到上一页(也就是webview所在的页面)。
页面({
data: { loading: true },
onLoad(选项){
const pages=GetcurrentPages();
const WebViewPage=pages[pages . length-2];
webviewPage.setData(
{
应为真
},
()={
app .微信. navigateback();
}
);
},
});
让我们一行一行来看:
const pages=GetcurrentPages();
这样可以得到整个小程序的当前页面栈。由于我们只允许此页面来自小程序的网络视图页面,因此它的前一页必须是网络视图所在的页面:
const WebViewPage=pages[pages . length-2];
获取页面对象webviewPage后,调用其方法setData来更新一个值:
webviewPage.setData(
{
应为真
},
()={
app .微信. navigateback();
}
);
当shouldReattachWebview的值为true时,表示需要重新连接Webview。现在,该页面上的事件已经完成,请返回webview所在的页面
app/browser/index . js页面
我只保留核心代码,具体的逻辑直接写入代码。
页面({
数据: {
Shouldeattachwebview : false。//需要重新挂接webview组件吗?
Webview重新附加: false。//有没有附上过一次WebView
hidebiew : false//是否隐藏网络视图组件
},
昂秀(){
//如果网络视图需要重新附上
if(this。数据。HaThErattachwebview){
this.setData(
{
//隐藏网络视图
hideWebview: true,
},
()={
this.setData(
{
//隐藏之后立马显示它,此时完成一次网络视图的销毁,拿到了邮件中的数据
hideWebview: false,
webviewReattached: true,
},
()={
//拿到数据之后,处理canvasData
这个。handlecanvasdata();
}
);
}
);
}
},
//当网络视图被销毁时,该方法被触发
handlePostMessage:函数(e) {
const { data }=e.detail
if (Array.isArray(data)) {
const messages=data.map(item={
尝试{
const object=JSON。解析(项);
this.handleMessage(对象);
返回对象;
} catch(错误){
退货项目;
}
});
this.setData({
消息: [.消息],
});
}
},
//处理每一条消息
handleMessage:函数(消息){
const {action,data}=消息
//如果保存画布操作
if(action==' SaveCanvas '){
//将数据先缓存进突然的中
const { canvasData }=this.data
//app.checksum是我自己封装的方法,计算任何数据的校验和,我拿它来当作键
//这可以保证同一条数据只会被处理一次
const snapKey=app.checksum(数据);
//只要未处理过的数据,才需要再次数据
if (canvasData[snapKey]!==true) {
if(canvasData[SnapKey]==undefined){
//将数据从缓存进`快照` s中
//这也是我自己封装的一个方法,可以将数据缓存起来,并且只能被读取一次
app.snap(snapKey,data);
//设置canvasData中snapKey字段为`假
canvasData[SnapKey]=false;
this.setData({
canvasData,
});
}
}
}
},
//当网络视图被重新附上之后,画布数据已经被保存进突然的中了,
handlecanvasdataa :异步函数handleCanvasData() {
const { canvasData }=this.data
//从canvasData中拿到所有的关键,并过滤到已经处理过的数据
常量键=对象键. filter(key=canvasData[key]==false);
if(键。length===0){
返回;
}
对于(设I=0;长度;i=1) {
尝试{
const key=key[I];
const { url }=app.snap(密钥);
//通过自己封装的方法,将url(也就是Base64字符)保存至相册
const saved=await app。保存图像(URL);
//更新canvasData对象
canvasData[key]=true
this.setData({
canvasData
})
console.log('saved: ',已保存);
} catch(错误){
app。toast(错误。消息);
返回;
}
}
},
})
对应的index.wxml文件内容如下:
web-view src=' { { src } } ' wx : if=' { { src } } ' bind message=' handleposmessage ' wx : if=' {!hideWebview}}' /
11
2022-12
17
2022-03
18
2021-11
19
2021-06
16
2021-06
16
2021-06