18910140161

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

顺晟科技

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}}' /

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航