本文承接 上一篇 博文,上一篇博文介紹了跨域的相關概念、測試demo的相關配置和
JSONP
和CORS
兩種跨域方式的實現。本文主要介紹document.domain
、URL.hash
、cross-fragment
、window.name
和postMessage
這五種方式的跨域實現。html
若是 A
源和 B
源具備相同的父域名,經過設置 document.domain
屬性,就很容易使其相互通訊。在 HTML 規範中document.domain
是一個只讀屬性,現代瀏覽器容許將其設置爲父域名(不是頂級域名)。例如,一個是 www.myapp.com
的頁面,能夠設置爲 myapp.com
,而另外一個來自 sample.myapp.com
的頁面也能夠設置爲 myapp.com
,下圖展現了document.domain
的工做原理:html5
經過將不一樣子域名的 document.domain
屬性設置爲相同的父域名,來實現不一樣子域名之間的跨域通訊,這並不屬於同源策略限制的範疇。可是,嚴格來講,子域名跨域的解決方案最適用於內部應用之間的跨域通訊。git
訪問頁面:document_domain_test.ejsgithub
<button>測試</button> <div></div> <iframe id="ifr" src="http://sample.myapp.com/document_iframe"></iframe> <script> document.domain = "myapp.com"; function sayHello(str) { document.querySelector('div').innerHTML = str; } document.querySelector('button').onclick = function() { document.querySelector('#ifr').contentWindow.sayHello('Hello son'); } </script>
跨域iframe:document_iframe.ejs跨域
document.domain = 'myapp.com'; function sayHello(str) { document.querySelector('div').innerHTML = str; parent.sayHello('Hello father'); }
效果:瀏覽器
一個 URL
由下圖所示的幾個部分組成:安全
通常來講, URL
的任何改變都從新會加載一個新的網頁,除了 hash
的變化, hash
的任何改變都不會致使頁面刷新。 hash
已經被普遍使用在支持局部刷新的 SPA
應用中,用來記錄用戶的訪問路徑。在跨域解決方案中, hash
也很是有用,來自不一樣源的頁面能夠相互設置對方的 URL
,包括 hash
值,但仍被限制獲取對方的 hash
值。文檔之間能夠經過 hash
來相互通訊。以下圖所示的例子:app
訪問頁面:url_hash_test.ejsdom
<button>發送消息</button> <iframe id="ifr" src="http://www.otherapp.com/hash_iframe"></iframe> <script> var target = "http://www.otherapp.com/hash_iframe"; function sendMsg(msg) { var data = {msg: msg}; var src = target + "#" + JSON.stringify(data); document.getElementById('ifr').src = src; } document.querySelector('button').onclick = function() { sendMsg("Time: " + (new Date())); } </script>
跨域iframe:hash_iframe.ejside
<div></div> <script> var oldHash = ""; var target = "http://www.myapp.com/url_hash_test"; var checkMessage = function() { var newHash = window.location.hash; if (newHash.length > 1) { newHash = newHash.slice(1, newHash.length); if (newHash !== oldHash) { oldHash = newHash; var msgs = JSON.parse(newHash); var msg = msgs.msg; sendMessage("Hello-father"); document.querySelector('div').innerHTML = msg; } } } setInterval(checkMessage, 1000); function sendMessage(msg) { var hash = "msg=" + msg; parent.location.href = target + "#" + hash; } </script>
主頁面經過改變 iframe
的 hash
值向 iframe
傳遞參數, iframe
不斷獲取本身的 hash
值,一旦發生變化就當即顯示主頁面傳來的消息,而且經過設置主頁面的 hash
就能夠像主頁面傳遞消息了,這樣實際就能夠完成雙向的跨域通訊了。
效果:
因爲許多網站的 hash
已經被用於其餘用途,對於這樣的網站用 hash
跨域將很是複雜(須要從 hash
中合併和分離出消息)。所以咱們就有了基於 hash
的一個升級版: cross-fragment
,其原理以下圖所示:
訪問頁面:cross_fragment_test.ejs
<button>發送消息</button> <div></div> <iframe id="otherapp" name="otherapp" src="http://www.otherapp.com/fragment_iframe"></iframe> <script> function sendMsg(msg) { var frame = document.createElement("iframe"); var baseProxy = "http://www.otherapp.com/fragment_req_proxy"; var request = {frameName: "otherapp", data: msg}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; document.body.appendChild(frame); } // 響應處理函數 function getResponse(data) { document.querySelector('div').innerHTML = "收到結果:" + data; } document.querySelector('button').onclick = function() { // 發送一個隨機數 sendMsg(Math.random()); } </script>
請求代理頁面:fragment_req_proxy.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; //目標頁面的getData方法 parent.frames[obj.frameName].getData(data); } </script>
跨域iframe:fragment_iframe.ejs
<div></div> <script> function getData(data) { document.querySelector('div').innerHTML = "收到的消息:" + data; var frame = document.createElement("frame"); var baseProxy = "http://www.myapp.com/fragment_res_proxy"; // 將接收的數據擴大一百倍 var request = {data: data * 100}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; document.body.appendChild(frame); } </script>
響應代理頁面:fragment_res_proxy.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; //主頁面的getResponse方法 parent.parent.getResponse(data); } </script>
這個例子中先在主頁面(來自myapp)放置一個otherapp下的 iframe
(目標頁面),點擊按鈕後就在主頁面中構造一個隱藏的iframe
(和目標頁面同域,來自otherapp,請求代理頁面),經過這個請求代理頁面調用目標頁面的 getData
方法,這個方法接收傳來的數據,處理完成後,構造一個隱藏的 iframe
(和主頁面同域,來自myapp,響應代理頁面),經過響應代理頁面調用主頁面中的 getResponse
方法。
效果:
以前整理過一個利用該方法實現 iframe自適應 的博文,有興趣的能夠看一下
window.name
跨域是一個巧妙的解決方案,咱們通常使用 window.name
的狀況以下:
window.frames[windowName]
獲得一個子窗口target
屬性加載任何頁面 window.name
的值始終保持不變。因爲 window.name
這個顯著的特色,使其適用於在不一樣源之間進行跨域通訊,但這是個不經常使用的屬性。那麼怎麼在同源策略下使用呢?下圖顯示瞭如何使用 window.name
來跨域通訊。
當頁面 A
想要從另外一個源獲取資源或Web服務,首先在本身的頁面上建立一個隱藏的 iframe
B,將 B 指向外部資源或服務,B 加載完成以後,將把響應的數據附加到 window.name
上。因爲如今 A 和 B 還不一樣源,A 依舊不能獲取到 B 的 name
屬性。當 B 獲取到數據以後,再將頁面導航到任何一個與 A 同源的頁面,這時 A 就能夠直接獲取到 B 的 name
屬性值。當 A 獲取到數據以後,就能夠隨時刪掉 B。
訪問頁面:window_name_test.ejs
<button>發送消息</button> <div class="req"></div> <div class="res"></div> <script> function sendMsg(msg) { var state = 0, data; var frame = document.createElement("frame"); var baseProxy = "http://www.otherapp.com/window_iframe"; var request = {data: msg}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; frame.onload = function() { if (state === 1) { data = frame.contentWindow.name; document.querySelector('.res').innerHTML = "得到響應:" + data; // 刪除iframe frame.contentWindow.document.write(''); frame.contentWindow.close(); document.body.removeChild(frame); } else { state = 1; frame.src = "http://www.myapp.com/window_iframe"; } }; document.body.appendChild(frame); } document.querySelector('button').onclick = function() { var val = Math.random(); sendMsg(val); document.querySelector('.req').innerHTML = "請求數據:" + val; } </script>
跨域iframe:window_iframe.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; window.name = data*100; } </script>
效果:
HTML5 規範中的新方法 window.postMessage(message, targetOrigin)
能夠用於安全跨域通訊。當該方法被調用時,將分發一個消息事件,若是窗口監聽了相應的消息,窗口就能夠獲取到消息和消息來源。以下圖所示:
若是 iframe
想要通知不一樣源的父窗口它已經加載完成,可使用 window.postMessage
來發送消息。同時,它也將監聽回饋消息。
訪問頁面:postMessage_test.ejs
<div></div> <iframe name="otherApp" id="otherApp" src="http://www.otherapp.com/postMessage_iframe"></iframe> <script> function handleReceive(event) { if (event.origin != "http://www.otherapp.com") return; // 處理數據 var data = JSON.parse(event.data); document.querySelector('div').innerHTML = "來自iframe的顏色:" + data.color; var otherAppFrame = window.frames["otherApp"]; otherAppFrame.postMessage("ok", "http://www.otherapp.com"); } window.addEventListener("message", handleReceive, false); </script>
跨域iframe:postMessage_iframe.ejs
<div></div> <script> function postMessage(msg) { var targetWindow = parent.window; targetWindow.postMessage(msg, "*"); } function handleReceive(msg) { if (msg.data === "ok") { document.querySelector('div').innerHTML = "消息發送成功"; } else { postMessage(JSON.stringify({color: 'red'})); } } window.addEventListener("message", handleReceive, false); window.onload = function() { postMessage(JSON.stringify({color: 'red'})); } </script>
效果:
本文介紹了經常使用的五種跨域實現方法,測試源碼 請戳這 !