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

栏目: Html5 · 发布时间: 5年前

内容简介:首先还原下业务场景,具体场景:QQ音乐页面与QQ音乐播放器,当QQ音乐页面添加歌曲时,QQ音乐播放器会自动将所操作的音乐添加至播放列表。上述业务场景就是,浏览器中的多个选项卡或窗口之间进行通信(在同一个域上,而不是CORS)的具体应用。

首先还原下业务场景, main page 主页面的某些操作会使得游览器打开新的 other page 标签页,之后当 main page 发生其他操作时候, other page 也要发生变动。

具体场景:QQ音乐页面与QQ音乐播放器,当QQ音乐页面添加歌曲时,QQ音乐播放器会自动将所操作的音乐添加至播放列表。

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

上述业务场景就是,浏览器中的多个选项卡或窗口之间进行通信(在同一个域上,而不是CORS)的具体应用。

其本质上都是利用一个中间介质进行数据传输,而不同介质则会导致其适用场景的不尽相同,常用的有如下几个解决方案:

window
postMessage
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 ,防止重复打开;

坑三:

需求设定子页面不能刷新,如果父页面发生刷新, windowObjectReferencenull ,该如何通信

解决方案是利用子页面的 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 是相同域名和端口的不同页面间可以共享。

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux Device Drivers

Linux Device Drivers

Jonathan Corbet、Alessandro Rubini、Greg Kroah-Hartman / O'Reilly Media / 2005-2-17 / USD 39.95

Device drivers literally drive everything you're interested in--disks, monitors, keyboards, modems--everything outside the computer chip and memory. And writing device drivers is one of the few areas ......一起来看看 《Linux Device Drivers》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具