跨页面通信 (postMessage)
场景
最近在工作中碰到这样一个业务场景。
有 A、B 两个页面(不同域名)。A 是一个显示信息页面;B 是一个电话呼叫系统。
存在问题:
A 页面中可以查看当前需要拨打的客户的电话号码。
B 页面用于打对外客服电话,由于己方客服可代表多个角色,所以在每次呼出电话前要手动选择己方角色。当角色和号码很多时,客服在使用系统时很容易选错角色或者拨错号码。
需求:
由于存在上述问题,所以希望实现这样功能:从 A 页面提供一个链接,点击该链接后跳转到 B 页面,跳转后的 B 页面中的己方角色已经选好,并且待呼出号码也填好。
思路
1. 利用 url 传值
我看到这个问题第一个想法就是给 A 页面增加一个跳转链接,通过 window.location 跳转过去,将所需数据放在 url 的 hash 值里面传递,然后在 B 页面中取出 hash 里的值做处理。A 页面语法如下:
window.location.href = 'https://www.xxx.com#id=1&name=jaakko'
在 B 页面就可以取出 hash 里的值:
window.location.hash
// TODO
由于 hash 值不会引起页面刷新,所以可以放心利用 hash 传值。
于是我兴高采烈地尝试了一下,结果让我很失望。问题在于:
B 页面是一个很古老的系统,该系统没有保存登录状态,这意味着,首次进入该页面或每次刷新页面时,都需要重新登录。所以每次跳转过去时,都需要重新登录,这有违我们减少工作量的初衷,所以只有另辟蹊径了。
2. 利用 postMessage 传值
第一次尝试失败后,我又想到了利用 postMessage 传值。
关于 postMessage 这里有一篇详细的介绍。
这里我就不详细介绍了,简单说一下。postMessage 用于实现页面间跨域通信,由于它可以捕获通信对方来源,所以相当于提供了一种安全机制。
发送方
语法
win.postMessage(msg, targetOrigin, [transfer]);
参数
win
win表示对目标窗口的一个引用,这个值可以是iframe的contentWindow属性、执行 window.open 返回的窗口对象、或是命名过或数值索引的 window.freames
msg
msg是需要传递的数据,这个值可以是对象
targetOrigin
通过窗口的 origin 属性可以指定哪些窗口能接到消息事件,可以为通配符
*(表示所有窗口),也可以为一个明确的url
[transfer]
是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权
接收方
语法
window.addEventListener("message", function (event) {
// TODO
});
event 对象有以下属性:
- data:从发送方传过来的数据
- origin:发送方窗口的
origin - source:发送方
window对象的引用,可通过该属性向发送方发送消息
demo
发送方 sender.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>发送方</title>
</head>
<body>
<h1>发送方</h1>
<input type="text" id="msg">
<button id="send">sendMsg</button>
</body>
<script>
const input = document.getElementById('msg');
const btn = document.getElementById('send');
const win = window.open('./receiver.html', '_blank'); // 获取接收方窗口对象
btn.addEventListener('click', () => {
let msg = input.value;
win.postMessage(msg, '*');
})
</script>
</html>
接收方 receiver.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>接收方</title>
</head>
<body>
<h1>接收方</h1>
<div id="content"></div>
</body>
<script>
const content = document.getElementById('content');
window.addEventListener('message', event => {
console.log(event);
content.innerText = event.data;
})
</script>
</html>