不一样页面通讯与跨域

0. 前言

相信跨域有什么手段,你们都背得倒背如流了。如今咱们来作一些不在同一个tab页面或者跨域的实践。javascript

1. localstorage

1.1 onstorage事件

localstorage是浏览器同域标签共用的存储空间,因此能够用来实现多标签之间的通讯。html5出现了一个事件: onstorage,咱们在window对象上添加监听就能够监听到变化: window.addEventListener('storage', (e) => console.log(e))html

须要注意,此事件是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数。若是实在是要,本身重写一个方法吧,要不就在修改的时候把本身改的内容po上去。vue

示例: js:html5

if(!localStorage.getItem('a')){
	localStorage.setItem('a',1)
}else{
	var s = localStorage.getItem('a')
	localStorage.setItem('a',+s+1)
}
window.addEventListener('storage', (e) => console.log(e))
复制代码

咱们新建两个html分别叫1.html和2.html,并加上上面的js,因而咱们每次打开或者刷新该页面就会给a加上1。须要注意的是,若是是双击打开,是在file://协议下的,并且不会触发storage事件,可是会给a加上1,因此能够作一个功能,计算本地某个文件被打开了多少次。若是咱们用服务器打开,咱们的不一样tab页面通讯完成了,并且是实时的。java

2. 玩转iframe

咱们都知道frame能够跨域,那么咱们来试一下。下面例子,都是一个html内嵌iframe,固然你直接打开iframe那个文件,没什么意义的node

2.1 利用hash变化传递信息实现父子窗口通讯(能跨域)

父窗口:1.htmlreact

html:git

<iframe name="myIframe" src="http://localhost:1000/2.html"></iframe>
复制代码

js:github

var originURL = myIframe.location.href
var i = document.querySelector('iframe')
i.onload= function(){//这是异步加载的iframe
  i.src += '#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
}
复制代码

子窗口:2.htmlweb

window.onhashchange = function() {
    console.log(window.location.hash)
}
复制代码

咱们打开父窗口,发现子窗口的js已经跑起来了。

既然能跨域,咱们直接双击打开1.html,发现仍是能够,这个例子双击打开和服务器打开都能达到目的

2.2 父调用子页面的js或者反过来调用

父调子:仍是基于前面的条件

var i = document.querySelector('iframe')
i.onload= function(){
myIframe.window.f();
}
复制代码

子:2.html

function f(){
	console.log('this is 2.html')
}
复制代码

子调父: 子:2.html

parent.fn1()
复制代码

父:1.html

function fn1(s){
	console.log('this is 1.html')
}
}
复制代码

固然,你直接打开2.html是没意义的并且是报错:Uncaught TypeError: parent.fn1 is not a function

这个须要注意,不能跨域,因此双击打开以及不一样域是报错的:Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.,只能服务器打开

2.3 window.name (能跨域)

相似于vue、react的prop父子传值,只要在父窗口设置iframe标签的name,在子窗口就能够读到。

父窗口:1.html

<iframe  name="myIframe" src="http://localhost:1000/2.html"></iframe>
复制代码

子窗口:2.html

console.log(window.name)
复制代码

少年,放心地双击打开吧,有效果的。

2.4 postmessage(能跨域)

H5以后为window新增了window.postMessage()方法,第一个参数是要发送的数据,第二个参数是域名。

父窗口:1.html

<iframe id="test" name="myIframe" src="http://localhost:1000/2.html"></iframe>

//js
var frame = document.querySelector('iframe')
frame.onload = function(argument) {
	window.frames[0].postMessage('data from html1', '*');
}
复制代码

子窗口:2.html

window.onmessage = function (e) {
	console.log(e.data)
}
复制代码

能够跨域,因此能直接双击打开能够看见效果。

上面的父子窗口,是指一个html里面的iframe标签引入另外一个html。

3. 非同域的两个tab页面通讯

也就是两个毫无关系的tab页面通讯(好比我打开一个baidu和一个github),怎么通?

固然baidu和github能不能通讯,咱们不知道,得问他们家的开发。前面咱们已经知道,iframe能跨域,localstorage能使得两个tab页面通讯。那咱们就来试一下,iframe桥接两个互不相干的tab页面。注意,bridge是一个html,其余两个tab是指浏览器打开的两个html文件。你能够另外创建两个不一样的html,也能够创建两个如出一辙的html,而后双击打开也好、服务器打开也好,有两个就能够了。

下面,咱们把桥接的iframe叫作bridge.html吧。咱们用node打开,监听本地的1000端口。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>hi</h1>
  </body>
  <script type="text/javascript"> window.addEventListener("storage", function(ev){ if (ev.key == 'info') { window.parent.postMessage(ev.newValue,'*'); } }); window.addEventListener('message',function(e){ // 接受到父文档的消息后,广播给其余的同源页面 localStorage.setItem('info',e.data); }); </script>
</html>
复制代码

咱们再来两个页面,内容以下。接着咱们能够以n种不一样方式分别打开,反正是非同源就能够了

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
    <input id="ipt" type="text" name="">
    <button onclick="sub()">sub</button>
    <p id="cont"></p>
    <iframe src="http://localhost:1000/" style="display: none"></iframe>
</body>
<script type="text/javascript"> var ipt = document.querySelector('#ipt') function sub(){ document.querySelector('iframe').contentWindow.postMessage(ipt.value,'*'); cont.innerHTML +='我:'+ ipt.value + '<br>' ipt.value = '' } window.addEventListener('message',function(e){ if(e.data) cont.innerHTML +='对方:'+ e.data + '<br>' }); </script>
</html>
复制代码

而后一个简单的聊天就搞定了,试试看。加上websocket的话,还能够非同源聊天呢,其余的能够本身随意设置了。

1

从1到2的信息实时传递更新就这样子成功了,反之亦然。

4.MessageChannel

顾名思义,信息通道。容许咱们建立一个新的消息通道,并经过它的两个MessagePort 属性发送数据m,并且在 Web Worker 中可用。能够控制台打印,发现有两个属性,portl1和port2。一个页面内嵌与iframe最经常使用这种方法。

就像一条管道,一边出一边进,咱们能够给postmessage方法加上第三个参数:

var channel = new MessageChannel();
channel.port1.addEventListener("message", function(e){
    window.parent.postMessage(e,'*',[channel.port2]);
    channel = null;
});
复制代码

深拷贝

n种不一样的对象类型?环引用?怎么作到特别容易的深拷?

然而真的作到了:

var obj ={a:1,b:2,c:{d:3,e:[{f:1,g:2}]},h:null}
obj.h = obj
var res 
new Promise(resolve => {
    var channel = new MessageChannel();
    channel.port2.onmessage = ev => resolve(ev.data);
    channel.port1.postMessage(obj);
  }).then(data=>{res = data})
复制代码

传了数据给管道,管道传回来一个长得如出一辙的数据回来,实现了深拷贝。咱们叫他结构化克隆,能处理对象循环依赖和大部分的内置对象。好比postMessage发消息给子窗口或者WebWorker的时候就会常常用到,拿到数据进行处理,但不污染原数据。

相关文章
相关标签/搜索