相信跨域有什麼手段,你們都背得倒背如流了。如今咱們來作一些不在同一個tab頁面或者跨域的實踐。javascript
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
咱們都知道frame能夠跨域,那麼咱們來試一下。下面例子,都是一個html內嵌iframe,固然你直接打開iframe那個文件,沒什麼意義的node
父窗口: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,發現仍是能夠,這個例子雙擊打開和服務器打開都能達到目的
父調子:仍是基於前面的條件
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.
,只能服務器打開
相似於vue、react的prop父子傳值,只要在父窗口設置iframe標籤的name,在子窗口就能夠讀到。
父窗口:1.html
<iframe name="myIframe" src="http://localhost:1000/2.html"></iframe>
複製代碼
子窗口:2.html
console.log(window.name)
複製代碼
少年,放心地雙擊打開吧,有效果的。
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。
也就是兩個毫無關係的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到2的信息實時傳遞更新就這樣子成功了,反之亦然。
顧名思義,信息通道。容許咱們建立一個新的消息通道,並經過它的兩個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的時候就會常常用到,拿到數據進行處理,但不污染原數據。