【跨域请求(二)】postMessage《双向跨域》

核心:
1. postMessage()方法:向其他窗口发送消息;
2. message事件:监听其他窗口(postMessage)发送过来的消息;
获取窗口的引用
1. iframe: window.frames[0]
2. iframe: contentWindowcontentDocument
3. iframe:class、id 等选择器;
4. 执行 window.open() 返回的窗口对象;
-------------👆父------------------------------👇子-------------
5. 新窗口获取原窗口: window.opener
6. message事件的回信对象: event.source


一、单向通信

1. 【打开B,B接收A的消息】

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页面发送消息</title>
	</head>
	<body>
		<input id="btn" type="button" value="打开 b 页面" />
		<script>
			var btn = document.querySelector('#btn');
			
			btn.addEventListener('click', function() {
				// 打开新窗口,并存储其引用(通过引用调用 postMessage)
				var pageB = window.open('http://bbb.com/b.html', '_blank');
				
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					// 调用 postMessage 函数,将消息发送给 pageB
					pageB.postMessage('该消息由A发送。', 'http://bbb.com/b.html');
					// 第二参数为接收消息的窗口的源
					// 1. 可以具体到某一个页面(url)
					// 2. 或者域名(该域下的所有页面都会接收)
					// 3. 或通配符 * (不限域,风险)
				}, 3000);
			});
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>b页面接收消息</title>
		<style>
			.msg  {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: red;
			}
		</style>
	</head>
	<body>
		<div class="msg"></div>
		<script>
			var msg = document.querySelector('.msg');
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从源发送来的消息
				if (e.origin !== 'http://aaa.com') return;
				
				// e.data 就是 A 页 postMessage 发送过来的内容
				msg.textContent = e.data;
				
				// e.source 是 A 窗口的引用
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>

2. 【打开B,A接收B的消息】

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页面接收消息</title>
		<style>
			.msg {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: red;
			}
		</style>
	</head>
	<body>
		<input id="btn" type="button" value="打开 b 页面" />
		<div class="msg"></div>
		<script>
			var btn = document.querySelector('#btn');
			var msg = document.querySelector('.msg');
			
			btn.addEventListener('click', function() {
				// 打开新窗口,不向该窗口发送消息
				window.open('http://bbb.com/b.html', '_blank');
			});
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从该源发送来的消息
				if (e.origin !== 'http://bbb.com') return;
				
				// e.data 就是 B 页 postMessage 发送过来的内容
				msg.textContent = e.data;
				
				// e.source 是 B 窗口的引用
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>b页面发送消息</title>
	</head>
	<body>
		<script>
			// window.opener 就是父窗口的引用
			var pageA = window.opener;
			
			// 检测当前窗口是否通过其他页面打开(是否有父窗口)
			if (pageA) {
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					pageA.postMessage('该消息由B发送。', 'http://aaa.com/a.html');
						// 第二参数为接收消息的窗口的源
						// 1. 可以具体到某一个页面(url)
						// 2. 或者域名(该域下的所有页面都会接收)
						// 3. 或通配符 * (不限域,风险)
				}, 3000);
			}
		</script>
	</body>
</html>

二、双向通信

1. 【打开B,B先接收A消息,再回复A】

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页面</title>
		<style>
			.msg  {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: blue;
			}
		</style>
	</head>
	<body>
		<input id="btn" type="button" value="打开 b 页面" />
		<div class="msg"></div>
		<script>
			var btn = document.querySelector('#btn');
			var msg = document.querySelector('.msg');
					
			btn.addEventListener('click', function() {
				// 打开新窗口,并存储其引用(通过引用调用 postMessage)
				var pageB = window.open('http://bbb.com/b.html', '_blank');
				
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					// 调用 postMessage 函数,将消息发送给 pageB
					pageB.postMessage('该消息由A发送。', 'http://bbb.com/b.html');
					// 第二参数为接收消息的窗口的源
					// 1. 可以具体到某一个页面(url)
					// 2. 或者域名(该域下的所有页面都会接收)
					// 3. 或通配符 * (不限域,风险)
				}, 3000);
			});
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从该源发送来的消息
				if (e.origin !== 'http://bbb.com') return;
				
				// e.data 就是 B 页 postMessage 发送过来的内容
				msg.textContent = e.data;
				
				// e.source 是 B 窗口的引用
			}
			
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>b页面</title>
		<style>
			.msg  {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: red;
			}
		</style>
	</head>
	<body>
		<div class="msg"></div>
		<script>
			var msg = document.querySelector('.msg');
			
			// 用于存储父窗口的引用
			var pageA = window.opener;
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从该源发送来的消息
				if (e.origin !== 'http://aaa.com') return;
				
				// e.data 就是 A 页 postMessage 发送过来的内容
				msg.textContent = e.data;

				// e.source 是 A 窗口的引用
					// console.log(e.source === pageA); // true
				e.source.postMessage('B收到并立即回复。', e.origin);
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>

2. 【打开B,A先接收B消息,再回复B】

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页面</title>
		<style>
			.msg {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: red;
			}
		</style>
	</head>
	<body>
		<input id="btn" type="button" value="打开 b 页面" />
		<div class="msg"></div>
		<script>
			var btn = document.querySelector('#btn');
			var msg = document.querySelector('.msg');
			
			// 用于存储子窗口的引用
			var pageB = null;
			
			btn.addEventListener('click', function() {
				// 打开新窗口,不立即向该窗口发送消息
				pageB = window.open('http://bbb.com/b.html', '_blank');
			});
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从该源发送来的消息
				if (e.origin !== 'http://bbb.com') return;
				
				// e.data 就是 B 页 postMessage 发送过来的内容
				msg.textContent = e.data;
				
				// e.source 是 B 窗口的引用
					// console.log(e.source === pageB);	// true
					e.source.postMessage('A收到并立即回复。', e.origin);
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			.msg {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: blue;
			}
		</style>
	</head>
	<body>
		<div class="msg"></div>
		<script>
			var msg = document.querySelector('.msg');
			
			// window.opener 就是父窗口的引用
			var pageA = window.opener;
			
			// 检测当前窗口是否通过其他页面打开(是否有父窗口)
			if (pageA) {
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					pageA.postMessage('该消息由B发送。', 'http://aaa.com/a.html');
						// 第二参数为接收消息的窗口的源
						// 1. 可以具体到某一个页面(url)
						// 2. 或者域名(该域下的所有页面都会接收)
						// 3. 或通配符 * (不限域,风险)
				}, 3000);
			}
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从该源发送来的消息
				if (e.origin !== 'http://aaa.com') return;
				
				// e.data 就是 A 页 postMessage 发送过来的内容
				msg.textContent = e.data;
				
				// e.source 是 A 窗口的引用
				// console.log(e.source === pageA);	// true
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>

三、接收多个页面发送的消息

【A页接收B、C的消息】

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页面接收消息</title>
		<style>
			.msg_b {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: red;
			}
			.msg_c {
				font-size: 3em;
				font-weight: bold;
				text-align: center;
				color: blue;
			}
		</style>
	</head>
	<body>
		<input id="btn" type="button" value="同时打开 b、c 页面" />
		<div class="msg_b"></div>
		<div class="msg_c"></div>
		<script>
			var btn = document.querySelector('#btn');
			var msg_b = document.querySelector('.msg_b');
			var msg_c = document.querySelector('.msg_c');
			
			btn.addEventListener('click', function() {
				// 打开新窗口,不向该窗口发送消息
				window.open('http://bbb.com/b.html', '_blank');
				window.open('http://ccc.com/c.html', '_blank');
			});
			
			// 监听 B 页发送过来的消息
			window.addEventListener('message', function(e) {
				// 筛选出 B 发送过来的消息
				if (e.origin !== 'http://bbb.com') return;
				msg_b.textContent = e.data;
			}, false);
			
			// 监听 C 页发送过来的消息
			window.addEventListener('message', function(e) {
				// 筛选出 C 发送过来的消息
				if (e.origin !== 'http://ccc.com') return;
				msg_c.textContent = e.data;
			}, false);
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>b页面发送消息</title>
	</head>
	<body>
		<script>
			// window.opener 就是父窗口的引用
			var pageA = window.opener;
			
			// 检测当前窗口是否通过其他页面打开(是否有父窗口)
			if (pageA) {
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					pageA.postMessage('该消息由B发送。', 'http://aaa.com/a.html');
						// 第二参数为接收消息的窗口的源
						// 1. 可以具体到某一个页面(url)
						// 2. 或者域名(该域下的所有页面都会接收)
						// 3. 或通配符 * (不限域,风险)
				}, 3000);
			}
		</script>
	</body>
</html>
  • c.html【C域】(http://ccc.com/c.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>c页面发送消息</title>
	</head>
	<body>
		<script>
			// window.opener 就是父窗口的引用
			var pageA = window.opener;
			
			// 检测当前窗口是否通过其他页面打开(是否有父窗口)
			if (pageA) {
				// 3秒后向该窗口发送消息
				setTimeout(function() {
					pageA.postMessage('该消息由C发送。', 'http://aaa.com/a.html');
						// 第二参数为接收消息的窗口的源
						// 1. 可以具体到某一个页面(url)
						// 2. 或者域名(该域下的所有页面都会接收)
						// 3. 或通配符 * (不限域,风险)
				}, 3000);
			}
		</script>
	</body>
</html>

四、利用 postMessage 读写其他窗口的 LocalStorage


传送门:浏览器同源政策及其规避方法 / 3.4 LocalStorage

  • a.html【A域】(http://aaa.com/a.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>a页读写其它页面localStorage</title>
	</head>
	<body>
		<input id="btn" type="button" value="向B页发送消息" />
		<iframe style="display: none;" src="http://bbb.com/b.html"></iframe>
		<script>
			var btn = document.querySelector('#btn');
			
			var pageB = document.getElementsByTagName('iframe')[0].contentWindow;
			// var pageB = window.frames[0];
			
			// 需要发送的数据
			var obj = {name: 'Jack', foo:'bar'};
			// var foo = {id: 'aaa'};
			
			btn.addEventListener('click', function() {
				// key: 'storage' 是存入 localStorage 的键名,数据均存储在同一行
				// 若需要存第二条 localStorage,则设置为其他 key 名,如:key: 'demo'
				
				// 存储 localStorage(发送消息,修改数据)
				pageB.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com/b.html');
				// pageB.postMessage(JSON.stringify({key: 'demo', method: 'set', data: foo}), 'http://bbb.com/b.html');
				
				// 获取 localStorage(发送消息,获取数据,本页 message 监听读取)
				pageB.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*");
				// pageB.postMessage(JSON.stringify({key: 'demo', method: "get"}), "*");
				
				// 删除 localStorage(发送消息,删除数据)
				pageB.postMessage(JSON.stringify({key: 'storage', method: "remove"}), "*");
				// pageB.postMessage(JSON.stringify({key: 'demo', method: "get"}), "*");
			});
			
			function receiveMessage(e) {
				if (e.origin !== 'http://bbb.com') return;
				
				// 读取某个数据
				console.log(JSON.parse(e.data).name);
				
				// 读取所有 localStorage 数据
				console.log(JSON.parse(e.data));
			}
			
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>
  • b.html【B域】(http://bbb.com/b.html)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>b页存储本页localStorage</title>
	</head>
	<body>
		<script>
			var msg = document.querySelector('.msg');
			
			// 接收消息
			function receiveMessage(e) {
				// 过滤掉不是从源发送来的消息
				if (e.origin !== 'http://aaa.com') return;
				
				var payload = JSON.parse(e.data);
				
				switch (payload.method) {
					case 'set':
						localStorage.setItem(payload.key, JSON.stringify(payload.data));
						break;
					case 'get':
						var parent = window.parent;
						var data = localStorage.getItem(payload.key);
						parent.postMessage(data, e.origin);
						break;
					case 'remove':
						localStorage.removeItem(payload.key);
						break;
				}
			}
			
			// message 事件,监听其它页面 postMessage 发送过来的消息
			window.addEventListener('message', receiveMessage, false);
		</script>
	</body>
</html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值