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里面的数据’}>>>
缺点:

  1. title字符长度有限制,具体看这张图,不过这个长度,很多人应该也够用了
  2. uniapp watch如何监听本地存储getStorageSync数据变化_uni-app

  3. 部分网页可能需要这个标题,或者他们直接将标题直接展示在页面上。不过应该很少吧

2.通过url拦截

父窗口也是可以拦截子窗口的url的
我们只需要发起一次拦截,拦截所有"getData://"协议的。
子窗口 window.location = 'getData://'+JSON.stringify({'height': 100})缺点:

  1. 部分网页在没加载完成前,使用window.location会出错。我测试的网页不多,但就发现了问题。在访问“慢慢买”的商品历史价格查询,第一次访问,会跳转到验证页面。在验证页面的验证模块加载前使用“window.location”,就会出错
  2. 占用了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

uniapp watch如何监听本地存储getStorageSync数据变化_数据_02


,上图很正常是吧,但是当我们后退到页面A时,请看下图。下图是页面A

uniapp watch如何监听本地存储getStorageSync数据变化_webview_03


,这个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的延迟,不过我的业务对延迟不敏感,所以不在乎