背景
最近在开发的时候遇见一些有趣的问题,其中之一就是在 IE11 上有些功能不可用,怎么说呢,具体要讲一下关于 window.opener
与 window.opener.postMessage
这两个特性.
解决问题的思路便是,不论你怎么想的,构造一个 demo 是有效且强大的方式之一。
使用场景: 在登录之后的跳转成功页面需要发一条消息给登录界面,告诉登录界面登录成功。
例如登录页面的URL: https://test.com/oauth2/login?redirect_uri=https://client1.com/callback
1.在 https://test.com/oauth2/login
页面输入用户名,密码
2.点击登录按钮
3.登录成功会跳转到 redirect_uri
页面,那么这时在 https://client1.com/callback
页面使用window.opener.postMessage
发送一条消息
4.https://test.com/oauth2/login
页面会收到消息来进行后续的处理
构建一个 DEMO
需要构建两个页面, 登录页面A与跳转页面B:
页面A:
<!DOCTYPE html>
<head>
<title>main</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script>
var interval;
function msgHandler(ev) {
if (ev.data.message === "deliverResult") {
console.log("result: " + ev.data.result);
ev.source.close();
clearInterval(interval);
window.removeEventListener("message", msgHandler);
}
}
function signin() {
var child = window.open("https://test.com/oauth2/login?redirect_uri=https://client1.com/callback", "_blank");
interval = setInterval(function() {
try {
if (child.closed) {
console.log("Window was closed....");
clearInterval(interval);
window.removeEventListener("message", msgHandler);
return;
}
} catch (e) {
console.log("ERROR", e);
}
}, 500);
window.addEventListener("message", msgHandler);
}
</script>
</head>
<body>
<button onclick="signin()">Sign in</button>
</body>
点击 Sign in
按钮时,首先会打开一个新的 tab 页面,并在当前 window 添加一个监听器来监听从子页面过来的消息,如果子页面关闭,则清除监听器
页面B:
<!DOCTYPE html>
<head>
<title>child</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
window.opener && window.opener.postMessage({ message: "deliverResult", result: true }, "*");
</script>
</head>
<body>
<a href="http://www.example.com">Go to example.com</a>
Then click the browser Back button when ready.
</body>
子页面被打开时,会发送一条消息给父页面。
其中,
window.opener
兼容性:MDN-window.opener
window.postmessage
兼容性:MDN-window.postmessage
这个 Demo 可以完成一个简单的页面间通讯,并且工作于 Chrome 和 Safari, 虽然 MDN 上 window.opener
对于 IE 的支持是一个问号,但是实际上它是工作的。
然而在 IE11 上面”工作”的含义是父页面的domain和子页面的domain是在同一域名下。
在跨域的情况下,子页面 window.postmessage
的消息是无法发送给父页面的。
举例来说:
页面A和页面B运行在一台服务器上,访问域名是: https://serverA.com
页面A需要打开一个登陆页面链接为: https://test.com/oauth2/login?redirect_uri=https://client1.com/callback
而跳转页面是: https://client1.com/callback
这种情况下,我们会处在一个跨域的情况下发送消息,IE11不支持也就是页面A收不到跳转链接页面的消息,那么问题来了,为什么要解决这个问题?因为 IE11 的用户是我们不能放弃的用户群。如果你不在意这部分用户,可以不用太过关注。
解决方法
方法一
保证页面A需要打开的登录页面链接和页面A在同一域名之下,也就是在页面A所在的服务器上添加一个路由,例如: https://serverA.com/gotoSignin
, 而这个路由可以打开一个跨域的链接:https://test.com/oauth2/login?redirect_uri=https://client1.com/callback
. 也就是说:只要保证window.open
了一个同域名的链接,那么接下来无论中间会如何跳转,哪怕跳到天上去,父页面也可以收到子页面的消息。
优点: 依然可以使用 window.opener
和 window.postMessage
缺点: 需要额外添加一个服务器端的路由
方法二
使用 window.localStorage
保证子页面写入数据,父页面使用定时器循环读取,仍然需要保证 window.open
在同域名下。
类似的,写 cookie
也是可以的,只是我们不建议采用的原因取决于具体的使用场景:如果传输的信息安全性很高,我们不希望写入浏览器内。
以上
工作在前端的话,浏览器支持问题是一件有趣的事,也许你实现了功能,突然恍然大悟这玩意儿在 IEXX 下根本不支持。
解决的方法有很多,重要的走出你的第一步,构建一个 demo.