跨文档通信
跨文档通信指的是从页面A到页面B之间的通信,不需要借助服务器
能够实现的方法:
- 通过消息事件实现,不受跨域的限制,并且可实现双向通信
postMessage
MessageChannel
- 通过客户端存储实现,收到跨域的限制(只有同源的文档才可以访问同一块客户端存储)
cookie
localStrage
/sessionStroage
IndexedDB
- 在页面跳转时携带信息,不受跨域的限制,但是只能单向、单次通信
window.name
window.location.hash
postMessage
方法
postMessage
方法可以实现两个页面的跨文档通信,不受跨域的限制
发送:
otherWindow.postMessage(message, targetOrigin, [transfer]);
- 事件的发送方是
otherWindow
,它是其他窗口的引用,比如iframe的contentWindow
对象,或者是window.open
返回的窗口对象 message
会被自动序列化,所以可以发送字符串,也可以发送对象targetOrigin
可以是*
,表明无限制,或者是一个URI,发送消息的时需要目标窗口的协议、主机地址、端口三者都匹配才能成功。如果设置为*
有可能导致数据泄露transfer
用的不多,它会和message
一同传递,这些对象的所有权将被移交给接收方,发送一方不再保有所有权。后面在MessageChannel会用到。
window.location.origin
返回的就是URL协议+服务器名称+端口号
接受:
window.addEventListener("message", (e) => {}, false);
监听的message
事件,其事件对象e
包含的属性有:
data
,从其他window中传过来的对象origin
,消息来源的窗口的origin,这个字符串也是由URL协议+服务器名称+端口号
拼接而成,用来验证消息来源source
,对消息来源的窗口的引用,可以通过这个属性调用postMessage
方法回应消息,实现双向通信
例子:
Tab1.html:
<body>
<button id="btn1">打开新窗口</button>
<h1 id="h1"></h1>
<button id="btn2">发送消息</button>
<script>
const btn1 = document.querySelector('#btn1');
const btn2 = document.querySelector('#btn2');
const h1 = document.querySelector('#h1');
/**
* 通过postMessage进行通信
*/
let otherWindow;
// 打开Tab2
btn1.addEventListener('click', () => {
otherWindow = window.open('./demo30-2.html');
});
// 向Tab2发送消息
btn2.addEventListener('click', () => {
// postMessage的调用者是其他窗口的引用,比如iframe的contentWindow属性,window.open返回的窗口对象
// 指定目标窗口的targetOrigin
otherWindow.postMessage('hello from tab 1', window.location.origin);
console.log('发送成功')
});
// 监听message事件
window.addEventListener('message', e => {
// 判断消息来源
if (e.origin !== window.location.origin) {
return
}
h1.innerHTML += e.data.value + '<br>';
})
</script>
</body>
Tab2:
<body>
<h1 id="h1"></h1>
<script>
const h1 = document.querySelector('#h1');
/**
* 通过postMessage进行通信
*/
// 监听message事件
window.addEventListener('message', e => {
// 验证消息来源
if (e.origin !== window.location.origin) {
return
}
h1.innerHTML += e.data + '<br>';
setTimeout(() => {
// e.source是消息来源的窗口的引用
e.source.postMessage({value: 'answer from tab2'}, window.location.origin)
}, 3000)
})
</script>
</body>
MessageChannel
MessageChannel
实际上也利用了postMessage的方法,通过实例化MessageChannel
对象,相当于建立了一个消息管道,实例有两个属性port1
和port2
,相当于管道的两个出口,我们利用这两个出口就可以实现跨文档通信
在当前文档,我们可以访问port1
,目标文档如何获得port2
呢?那就是通过前面提到的postMessage
的第四个参数transfer
,将port2
传递给目标文档,目标文档通过监听message
事件,事件对象e
的port
属性中就可以获得管道的另一个出口
在建立管道的过程需要获取目标文档,使用的是文档窗口的message
相关事件,待管道建立后就只需要使用管道的消息事件就可以了。通过port.postMessage
和port.onmessage
就可以实现跨文档通信
Tab1:
<body>
<button id="btn1">打开新窗口</button>
<h1 id="h1"></h1>
<button id="btn2">发送消息</button>
<script>
const btn1 = document.querySelector('#btn1');
const btn2 = document.querySelector('#btn2');
const h1 = document.querySelector('#h1');
let otherWindow;
/**
* MessageChannel
*/
const channel = new MessageChannel();
const {port1, port2} = channel;
btn1.addEventListener('click', () => {
// 新建页面
otherWindow = window.open('./demo30-2.html');
});
btn2.addEventListener('click', () => {
// 将port2传递给目标文档
otherWindow.postMessage('try to connect', window.location.origin, [port2])
});
// 监听port的message事件
port1.onmessage = function (e) {
h1.innerHTML += e.data + '<br>';
setTimeout(() => {
// 通过管道发送消息
port1.postMessage('Hello form tab1')
}, 2000);
}
</script>
Tab2:
<body>
<h1 id="h1"></h1>
<script>
const h1 = document.querySelector('#h1');
/**
* MessageChannel
*/
let port;
// 监听message事件,建立管道
window.addEventListener('message', e => {
// 验证消息来源
if (e.origin !== window.location.origin) {
return
}
h1.innerHTML += e.data + '<br>';
// 获取转移过来的端口
port = port || e.ports[0];
// 监听port的message事件
port.onmessage = function (e) {
h1.innerHTML += e.data + '<br>';
// 通过管道发送消息
setTimeout(() => {
port.postMessage('answer from tab2')
}, 2000)
};
//
setTimeout(() => {
port.postMessage('connection is ok')
}, 2000)
});
</script>
cookie
/localStorage
/sessionStroage
这三者本质是相同的,都可以在浏览器端进行保存并且访问
cookie本质上是服务器发送到浏览器的数据,在使用这三者进行跨文档通信收到跨域的限制。
cookie可以通过document.cookie
直接写入和读取,localStorage
和sessionStroage
有专门的API:
window.localStorage.setItem(key, value)
window.localStorage.getItem(key)
它们都只能存入字符串,如果需要存入对象,则需要手动将对象序列化。
IndexedDB
IndexedDB用于客户端存储大量结构化数据,是一个机遇JavaScript的面向对象的数据库。有了它就可以不考虑网络的可用性,创建可离线的Web应用程序。
具体的使用以后慢慢学习吧。
IndexedDB@MDN
基本概念@MDN
使用 IndexedDB@MDN
window.name
当通过更改window.location.href
或者<a>
标签在当前窗口载入新的页面时,window.name
会保留上个页面设置的信息
Tab1:
<body>
<a href="demo30-2.html">打开页面</a>
<script>
window.name = '123';
</script>
</body>
Tab2:
window.onload = function () {
console.log(window.name)
}
// 123
注意,这种方式必须是在当前页面打开的文档。
window.location.hash
可以将要传递的信息存储在window.location.hash
中
Tab1:
<a href="demo30-2.html#hello">打开页面</a>
Tab2:
window.onload = function () {
console.log(window.location.hash.slice(1))
}
注意,这种形式有可能会导致页面滚动到具有重名的标示符,同时会在浏览器的访问历史用增加一条记录