web-view是指<web-view src=""></webview>
webview是指通过plus.webview.create创建的frame窗口
web-view的通讯非常简单,这里就不多阐述了。
至于为什么不使用web-view,是因为bug满天飞(仅仅指窗口设置为随着父窗口滚动),反馈给官方,官方也不处理,所以才使用webview。但是webview的通讯真的很糟糕,之前还好,但在uni-app后,就很烂了
目前网上都有两种解决方法
1.通过改变title
webview是可以在父窗口监听子窗口的标题变化
如:淘宝网<<<{height: 100,text: ‘我就是躲在title里面的数据’}>>>
缺点:
-
title字符长度有限制,具体看这张图,不过这个长度,很多人应该也够用了
-
部分网页可能需要这个标题,或者他们直接将标题直接展示在页面上。不过应该很少吧
2.通过url拦截
父窗口也是可以拦截子窗口的url的
我们只需要发起一次拦截,拦截所有"getData://"协议的。
子窗口 window.location = 'getData://'+JSON.stringify({'height': 100})
缺点:
- 部分网页在没加载完成前,使用window.location会出错。我测试的网页不多,但就发现了问题。在访问“慢慢买”的商品历史价格查询,第一次访问,会跳转到验证页面。在验证页面的验证模块加载前使用“window.location”,就会出错
- 占用了url拦截事件,该事件只会触发一次。巧了,我也业务逻辑是必须在子窗口里面拦截,所以父窗口的拦截链接也失效了
3.把要通讯的数据保存到变量或者本地
可以通过改变title值,来触发监听。当然只需要在title后面加一个很小很小的符号(第一次加,第二次减),一般很难发现
我用这个 -> ·
通讯的数据,我是保存到本地(plus.storage.setItem)。当然保存到内存肯定是最快的,保存到本地的读写速度肯定没内存快。不过差距非常的小,可以忽略
下面的我的代码
//uni-app
this.lishijiaFrame.addEventListener('titleUpdate', (event) => {
var str = plus.storage.getItem('frameToDataWin');//获取保存在本地的通讯数据
if(str){
plus.storage.setItem('frameToDataWin', '')//清除保存在本地的通讯数据
var obj = JSON.parse(str);//字符串转json
if(typeof(obj.height) !== 'undefined'){
this.lishijiaWinHeight = obj.height
}
this.lishijiaFrame.evalJS('--set__Data_index;')//只有执行了这个,子窗口的下次通讯请求才会执行
}
}, false);
//h5
//向App传参
var set__Data_index=0,set__Data = function(data){
if(set__Data_index === 0){
plus.storage.setItem('frameToDataWin', JSON.stringify(data));//把传参数据保存到本地
if(new RegExp(/·$/).test(document.title)){//如果title最后一个字符是点的话,删除
document.title = document.title.replace(/·$/, '')
}else{
document.title += '·'//结尾添加小点【用这个符号是因为,这个符号特别小】
}
++set__Data_index;
}else{//不等于0,说明上次通讯还在进行中
setTimeout(set__Data, 1, data);
}
}
set__Data({height: 100});
优点:
无字符长度限制,兼容性高
缺点:
如果连续性的传参,如一次性执行set__Data({height: 100});set__Data({height: 200});set__Data({height: 300});
那么每次父窗口获取到新的通讯值之间,会有大概4毫秒的延迟。但是4毫秒超级低的,建议忽略掉。
而且4毫秒是相对于一款18-19年买红米 note 7 pro的1300元低端机型。如果放到稍微高端一点,可能延迟还不到1毫秒
================ 2022-05-07更新以下内容 ====================
更新一个bug(安卓,和win。ios不会)(该bug是谷歌或者uni-app的问题) 和 优化代码逻辑
先说优化代码逻辑,多个数据要一起通讯是需要排队的,之前排队是根据set__Data_index来判断的,现在改成下面的方式,执行逻辑更加清晰,效率也更高
//uni-app
frame.evalJS('xlg_emit_ranksList.shift();if(xlg_emit_ranksList[0] !== undefined){xlg_emit_ranksFn(xlg_emit_ranksList[0])}')//本次任务执行完毕,从队列中删除本次任务[也就是第一个任务]。如果存在下一个任务,便执行
//H5
window['xlg_emit_ranksList'] = [],
window['xlg_emit_ranksFn'] = (data) => {
plus.storage.setItem('frameToDataWin', JSON.stringify(data));//把传参数据保存到本地
itmeWin.beginPullToRefresh()//开启下拉
},
window['set__Data'] = (data) => {//向App传参
//存储
window['xlg_emit_ranksList'].push(data)
//执行
if(window['xlg_emit_ranksList'].length === 1){
window['xlg_emit_ranksFn'](window['xlg_emit_ranksList'][0])
}
};
下面说说bug
https://m.v.qq.com/ 这个页面是采用history路由模式,进行页面的前进和后退的。
当A页面前进到B页面之后,在B页面修改title后,再从B页面后退到A页面,标题会变成A页面的原始标题,但是执行document.title获取的,依旧是B面修改的title。所以这个bug好像是谷歌的问题,当然不信可以看下面的截图
下图是页面B
,上图很正常是吧,但是当我们后退到页面A时,请看下图。下图是页面A
,这个bug会导致使用document.title获取到的标题不是最新的,但是这个问题其实也是很好解决的,
因为uni-app是可以监听这个标题变化的,你再通过uni-app向H5执行一些代码(webview的evalJS方法),更新一下就行了
但是还有一个更致命的bug,当我从页面B回退到页面A,再从页面A前进到页面C之后,uni-app的标题监听就失效了,这个可能是uni-app的bug
所以,目前测试,只要把标题修改成原先的标题,就没事了。也就是一次通讯修改两次标题
代码如下
//uni-app
$winAndFrame_communicate(frame, fn){//子窗口 和 父窗口 通讯
frame.addEventListener('titleUpdate', (event) => {
if(new RegExp(/<[0-9]{17}>$/).test(event.title) === true){
var obj = JSON.parse(plus.storage.getItem('frameToDataWin') || '{}')
plus.storage.setItem('frameToDataWin', '');//清空保存的数据
frame.evalJS(`
xlg_emit_ranksList.shift();//删除数组的第一个元素,并重新排列数据
if(xlg_emit_ranksList[0] !== undefined){//如果还有数据,则执行
xlg_emit_ranksFn(xlg_emit_ranksList[0])
}
document.title = new RegExp(/<[0-9]{17}>$/).test(document.title) ? document.title.replace(/<[0-9]{17}>$/, '') : (document.title + '<' + (new Date().getTime() + '' + getRandomNunber(1000,9999)) + '>')//修改回原先的标题
`)
typeof(fn) === 'function' && fn(obj)//json字符串转对象
}
});
}
this.urlFrame = plus.webview.create()
this.$winAndFrame_communicate(this.urlFrame, (res) => {
console.log('这个是H5传递过来的数据', res);
})
//H5
getRandomNunber = (start, end) => {//随机数
return Math.floor(Math.random() * (end - start) + start)
},
window['xlg_emit_ranksList'] = [],
window['xlg_emit_ranksFn'] = (data) => {
plus.storage.setItem('frameToDataWin', JSON.stringify(data));//把传参数据保存到本地
document.title = new RegExp(/<[0-9]{17}>$/).test(document.title) ? document.title.replace(/<[0-9]{17}>$/, '') : (document.title + '<' + (new Date().getTime() + '' + getRandomNunber(1000,9999)) + '>')
},
window['set__Data'] = (data) => {//向App传参
//存储
window['xlg_emit_ranksList'].push(data)
//执行
if(window['xlg_emit_ranksList'].length === 1){
window['xlg_emit_ranksFn'](window['xlg_emit_ranksList'][0])
}
};
set__Data({name: 'height',data: 100});
但是也有缺点,那就是延迟更高的,100+ms的延迟,不过我的业务对延迟不敏感,所以不在乎