跨标签页通讯以及实战排坑

阿里云幸运券
业务场景
首先还原下业务场景,main page主页面的某些操作会使得游览器打开新的other page标签页,之后当main page发生其他操作时候,other page也要发生变动。
具体场景:QQ音乐页面与QQ音乐播放器,当QQ音乐页面添加歌曲时,QQ音乐播放器会自动将所操作的音乐添加至播放列表。

上述业务场景就是,浏览器中的多个选项卡或窗口之间进行通信(在同一个域上,而不是CORS)的具体应用。
其本质上都是利用一个中间介质进行数据传输,而不同介质则会导致其适用场景的不尽相同,常用的有如下几个解决方案:

使用 window 对象;
postMessage API;
使用 cookies;
使用 localStorage;

window 对象
利用Window 接口的 open() 方法,是用指定的名称将指定的资源加载到浏览器上下文(窗口 window ,内嵌框架 iframe 或者标签 tab ),同时该方法会返回一个打开的新窗口对象的引用。
// strUrl => 新窗口需要载入的url地址,strWindowName => 新窗口的名称
// strWindowFeatures => 是一个字符串值,这个值列出了将要打开的窗口的一些特性(窗口功能和工具栏)
let windowObjectReference = window.open(strUrl, strWindowName, [strWindowFeatures]);
// windowObjectReference 为打开的新窗口对象的引用
复制代码如果父子窗口满足“同源策略”,你可以通过这个引用访问新窗口的属性或方法。
也就是说,只用当两个 URL 具有相同的协议,域和端口时,才能利用 windowObjectReference 访问到页面的相关属性。
基于 windowObjectReference ,通常有两种解决跨标签页通讯的方法:

window.name 结合 setInterval:
父页面利用 windowObjectReference 修改 name 属性,子页面轮询
// 父页面
let windowObjectReference = null
document.getElementById(‘btn’).onclick = function() {
if (!windowObjectReference) {
windowObjectReference = window.open(’./other.html’, ‘Yang’)
} else {
windowObjectReference.name = windowObj.name + ‘NB’
}
}
// 子页面
setInterval(function() {
document.title = window.name
}, 1000)
复制代码
这种方法优点是向下兼容性好,缺点是利用 setInterval 开销较大、且 window.name 只能使用字符串;

window.location.hash 结合 window.addEventListener(“hashchange”, function, false)
父页面利用 windowObjectReference 动态修改 location.hash,子页面利用 window.addEventListener(“hashchange”, function, false) 监听 URL 的片段标识符更改。
// 父页面
let windowObjectReference = null
document.getElementById(‘btn’).onclick = function() {
if (!windowObjectReference) {
windowObjectReference = window.open(’./other.html’, ‘Yang’)
// 有坑
windowObjectReference.onload = function() {
windowObjectReference.location.hash = ‘Yang’
}
} else {
windowObjectReference.location.hash =
windowObjectReference.location.hash + ‘NB’
windowObjectReference.focus()
}
}
// 子页面
window.addEventListener(“hashchange”, function(e) {
document.title = window.location.hash
}, false);
复制代码
这种方法开销较小,且 URL 长度受限制。

坑一:
可以从代码中可以看出,首次修改 windowObjectReference.location.hash 时,利用了 onload 事件
windowObjectReference.onload = function() {
windowObjectReference.location.hash = ‘Yang’
}

腾讯云代金券

复制代码这是因为调用 window.open() 方法以后,远程 URL 不会被立即载入,载入过程是异步的。(实际加载这个URL的时间推迟到当前脚本块执行结束之后。窗口的创建和相关资源的加载异步地进行。);
坑二:
如果父页面刷新之后,重新 window.open 时,需要主要一定要设定 strWindowName,防止重复打开;
坑三:
需求设定子页面不能刷新,如果父页面发生刷新,windowObjectReference 为 null,该如何通信
解决方案是利用子页面的 window.opener,该属性能够返回打开当前窗口的那个窗口的引用,也就是父页面。
具体做法是在
// 子页面
setInterval(function() {
window.opener.windowObjectReference = window
}, 200)
复制代码坑四:
需求又说了点击后希望自动跳转到子页面,调用 windowObjectReference.focus()。
坑五:
坑三的赋值需要在 beforeunload 事件中 window.opener.windowObjectReference = null
postMessage API
这个解决方案最大的优点是可以安全地实现跨源通信,提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
// otherWindow 其他窗口的一个引用,如上述 windowObjectReference
otherWindow.postMessage(message, targetOrigin, [transfer]);
// message 将要发送到其他 window的数据
// targetOrigin 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI
复制代码利用上述API,我们可以完成通信工作
// 父页面
let windowObjectReference = null
document.getElementById(‘btn’).onclick = function() {
if (!windowObjectReference) {
windowObjectReference = window.open(’./other.html’, ‘Yang’)
windowObjectReference.onload = function() {
// windowObjectReference.location.hash = ‘Yang’
windowObjectReference.postMessage(‘Yang’, windowObjectReference.origin)
}
} else {
// windowObjectReference.location.hash = windowObjectReference.location.hash + ‘NB’
windowObjectReference.postMessage(‘NB’, windowObjectReference.origin)
windowObjectReference.focus()
}
}
// 子页面
window.addEventListener(“message”, function (e) {
document.title = e.data
// event.source.postMessage(‘string’, event.origin)
}, false);
复制代码同时还可以利用 event.source.postMessage(‘string’, event.origin) 完成双向通信(同域)。
PS: 需要特别注意一点,应用该方法时,一定要对 event.origin 进行过滤,具体可以参考 MDN。
cookies
cookies 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
用它来完成跨标签页通信,其实和 window.name 的应用方法差不多,在实际场景中 cookies 中保存着 session 、token 等登陆信息,一般不建议用来通信,可以用来父页面和子页面共享登陆信息。
localStorage
localStorage 需要结合 window.onstorage 就是,父页面修改 localStorage,子页面能够监听到它的变化。
// 父页面
let windowObjectReference = null
document.getElementById(‘btn’).onclick = function() {
if (!windowObjectReference) {
windowObjectReference = window.open(’./other.html’, ‘Yang’)
windowObjectReference.onload = function() {
localStorage.setItem(‘hero’, ‘Yang’)
}
} else {
localStorage.setItem(‘hero’, ‘NB’)
windowObjectReference.focus()
}
}
// 子页面
window.onstorage = function (e) {
document.title = e.newValue
}
复制代码还可以结合 JSON.stringify 传递对象等数据结构,由于利用了 window.onstorage 导致该方法的兼容性不是很好,同时 localStorage 是相同域名和端口的不同页面间可以共享。

总结
这篇文章的起源是在用QQ音乐时候,点击歌曲,会自动地添加到播放器中,然后不由自主地打开了 devtool。之后项目中遇到了这个场景,就实现了一次。

腾讯云代金券

原文链接

https://juejin.im/post/5c6d4e8f51882562ec59158e

服务推荐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值