js 同源页面间通信 storage 和跨域页面间通信 postMessage
同源页面间通信,storage事件监听介绍使用扩展跨域页面间通信 postMessage介绍使用安卓平台差异化处理安全问题同源页面间通信,storage事件监听参考博客:h5 storage事件监听介绍当同源页面的某个页面修改了localStorage,其余的同源页面只要注册了storage事件,就会触发,所以满足触发监听的条件有三个:同一浏览器打开了两个同源页面其中一个网页修改了localSto
同源页面间通信
(1) storage事件监听
参考博客:h5 storage事件监听
介绍
当同源页面的某个页面修改了localStorage
,其余的同源页面只要注册了storage
事件,就会触发,所以满足触发监听的条件有三个:
- 同一浏览器打开了两个同源页面
- 其中一个网页修改了localStorage
- 另一网页注册了storage事件
注意:在同一个网页修改本地存储,又在同一个网页监听,这样是没有效果的
使用
//A页面
window.addEventListener("storage", function (e) {
console.log(e);
});
//B页面
localStorage.clear();
localStorage.setItem('aaa', '111');
A页面打印如下:
我们可以根据传值让B页面控制A页面刷新:
//A页面
window.addEventListener("storage", function (e) {
if(e.key === 'freshen' && e.newValue === '刷新A页面'){
localStorage.setItem('freshen', '');
window.location.reload();
}
});
//B页面
localStorage.setItem('freshen', '刷新A页面');
扩展
如果非得要在同一网页监听怎么办?可以重写localStorage的方法,抛出自定义事件:
var orignalSetItem = localStorage.setItem;
localStorage.setItem = function (key, newValue) {
var setItemEvent = new Event("setItemEvent");
setItemEvent.newValue = newValue;
window.dispatchEvent(setItemEvent);
orignalSetItem.apply(this, arguments);
}
window.addEventListener("setItemEvent", function (e) {
console.log(e.key + '---' + e.newValue);
});
localStorage.setItem("aaa", "111");
(2) BroadcastChannel
介绍
允许同源的不同浏览器窗口,Tab 页,frame 或者 iframe 下的不同文档之间相互通信
使用
在A页面:
const bcA = new BroadcastChannel('aaa');
bcA.addEventListener('message', (event) => {
console.log(event);
});
//或
bcA.onmessage = (event) => {
console.log(event);
}
在B页面:
const bcB = new BroadcastChannel('aaa');
bcB.postMessage('New listening connected!');
即可在A页面收到信息
bcB.close()
关闭频道对象,告诉它不要再接收新的消息,并允许它最终被垃圾回收。
浏览器兼容(不支持ie)
跨域页面间通信 postMessage
参考博客:window.postMessage()
介绍
postMessage 可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 页面与嵌套的 iframe 消息传递
- 多窗口之间消息传递
语法
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
:
其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。
message
:
要发送的数据。它将会被结构化克隆算法序列化,所以无需自己序列化(部分低版本浏览器只支持字符串,所以发送的数据最好用JSON.stringify() 序列化)。
targetOrigin
:
通过 targetOrigin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串“*”(表示无限制)或者一个 URI(如果要指定和当前窗口同源的话可设置为"/")。在发送消息的时候,如果目标窗口的协议、主机地址或端口号这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会发送。
执行如下代码,其他 window 可以监听派遣的 message 获取发送过来的数据:
window.addEventListener("message", (event)=>{
var origin = event.origin
if (origin !== "http://example.org:8080")
return;
// ...
}, false);
使用 addEventListener 绑定事件,如果代码走多次,会导致监听绑定多次,在VUE中,当组件销毁时需要调用 removeEventListener 移除监听
还可以使用另一种监听写法,则不会重复绑定监听事件
window.onmessage=function(event){
var origin = event.origin
if (origin !== "http://example.org:8080")
return;
// ...
}
event 的属性有:
- data: 从其他 window 传递过来的数据
- origin: 调用 postMessage 时,消息发送窗口的 origin。例如:“http://example.com:8080”。
- source: 对发送消息的窗口对象的引用。可以使用此来在具有不同 origin 的两个窗口之间建立双向数据通信。
使用
通过window.open
打开的页面
父传子
//父页面
const targetWindow = window.open('http://www.bbb.com');
setTimeout(()=>{
targetWindow.postMessage('父传子', 'http://www.bbb.com')
}, 3000)
//子页面
window.addEventListener('message', (e) => {
console.log(e.data)
})
子传父
//父页面
window.open('http://www.bbb.com');
window.addEventListener('message', (e) => {
console.log(e.data)
})
//子页面
window.opener.postMessage('子传父', 'http://www.aaa.com')
两个窗口之间建立双向数据通信
/**
* localhost:10002/index页面
**/
// 接收消息
window.addEventListener('message', (e) => {
console.log(e.data)
})
// 发送消息
const targetWindow = window.open('http://localhost:10001/user');
setTimeout(()=>{
targetWindow.postMessage('来自10002的消息', 'http://localhost:10001')
}, 3000)
/**
* localhost:10001/user页面
**/
window.addEventListener('message', (e) => {
console.log(e.data)
if (event.origin !== "http://localhost:10002")
return;
e.source.postMessage('来自10001的消息', e.origin)
})
页面与嵌套的 iframe 消息传递
http://www.domain1.com/a.html
<iframe id="iframe" src="http://www.domain2.com/b.html"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
// 向domain2发送跨域数据
iframe.contentWindow.postMessage('来自domain1的消息', 'http://www.domain2.com');
};
// 接受domain2返回数据
window.addEventListener('message',(e) => {
console.log(e.data);
}, false);
</script>
http://www.domain2.com/b.html
<script>
// 接收domain1的数据
window.addEventListener('message',(e) => {
console.log(e.data);
if(e.origin !== 'http://www.domain1.com')
return;
// 发送消息给domain1
window.parent.postMessage('来自domain2的消息', e.origin);
}, false);
</script>
安卓平台差异化处理
/* Android 平台 Post Message 消息监听 Hook */
window.Android_handleMessage = message => {
// Android 使用 Base64 编码格式,需要先解码
let data = decodeURIComponent(escape(window.atob(message)));
};
安全问题
- 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件监听器。
- 如果你确实希望从其他网站接收message,请始终使用 origin 和 source 属性验证发件人的身份。
- 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)