由於瀏覽器的一種安全機制——同源策略的限制,致使不能直接獲取不一樣源的資源,因此要跨域。javascript
同源策略限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。
限制之一是不能經過ajax的方法去請求不一樣源中的文檔。
第二個限制是瀏覽器中不一樣域的框架(iframe)之間是不能進行js的交互操做的。html
那麼什麼才叫「同源」呢?前端
圖來自MDN,參見最後Reference.java
下面介紹經常使用的幾種跨域方法。node
原理:git
<script>
標籤不受瀏覽器同源限制的影響,<script>
發起的請求,因此會把請求到的內容看成js代碼來解析執行。缺點:只能發送get請求
下面是一個簡單的例子github
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> function callback (msg) { console.log('json:', msg) } </script> <script src="http://localhost:8080?cb=callback "> </script> </body> </html>
如上,定義了一個全局的callback 函數,而後利用<script>
來發起get請求,注意把函數名稱callback 一同發送給服務端,爲何呢,接着往下看服務端代碼ajax
//nodeJs var http = require('http'); var url = require('url'); /** * 客戶端請求的函數 * @param req * @param res */ function onRequest (req, res) { console.log("獲取到的請求參數的路徑:" + req.url); //獲得鍵值對 var arg = url.parse(req.url, true).query; //打印鍵值對中的值 console.log(arg.cb); res.writeHead(200); res.write(arg.cb + '("我今天要用jsonp來跨域獲取數據")'); res.end(); } http.createServer(onRequest).listen(8080);
服務端簡單解析cb參數(咱們傳的函數名稱),而後返回一段字符串callback("我今天要用jsonp來跨域獲取數據")
瀏覽器會收到響應以下:chrome
發現什麼了嗎?響應內容callback("我今天要用jsonp來跨域獲取數據")
會被看成js代碼來執行,正好調用了咱們以前定義的callback函數。
由此,咱們成功的利用jsonp經過跨域獲取到了想要的數據。json
開頭咱們說到不一樣源的框架之間是不能進行js交互操做的,實際上是能夠獲取window對象,但不能獲取window的屬性。
原理:
那麼主域名相同,子域名不一樣的框架之間跨域獲取數據的思路就來了,咱們把它們的document.domain都設置成主域名不就完事了?
好比有一個頁面a.google.com/1.html
這裏參考了其餘文章的代碼。出處見本文最下方
<iframe id = "iframe" src="http://b.google.com/2.html" onload = "test()"></iframe> <script type="text/javascript"> document.domain = 'google.com';//設置成主域 function test(){ alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 對象 } </script>
另外一個頁面b.google.com/2.html
只需設置主域名爲google.com
,兩個框架間就能夠進行交互啦。
<script type="text/javascript"> document.domain = 'google.com';//設置成主域 </script>
原理:
思路:
代碼以下:
父親窗口頁面
http://localhost:63342/test-field/cross-origin/local-test/hash-parent.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <iframe src="http://blank121.github.io/test-field/cross-origin/hash.html"> </iframe> <script> console.log('old hash:', location.hash) window.addEventListener('hashchange', function (ev) { console.log('new hash:', location.hash) }) </script> </body> </html>
子窗口頁面
http://blank121.github.io/tes...
try{ parent.location.hash='今天我要用hash跨域' //chrome ie 直接修改parent.location.hash報錯 }catch (e){ var iframe = document.createElement('iframe') iframe.src = 'https://localhost:63342/test-field/cross-origin/local-test/hash-proxy.html'+'#今天我要用hash跨域' document.body.appendChild(iframe); }
代理窗口頁面
http://localhost:63342/test-field/cross-origin/local-test/hash-proxy.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> console.log('proxy',location.hash) parent.parent.location.hash = location.hash </script> </body> </html>
如上,在子窗口頁面內修改了代理窗口的hash值,代理窗口又修改了父親窗口的hash值,父親窗口監聽hashchange就能夠獲取到不一樣源的子窗口傳來的數據啦。
控制檯結果以下:
原理:
window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的載入而進行重置。
利用一個窗口的生命週期內,載入不一樣頁面window.name不變的特性
貼代碼前首先去作個有趣的實驗
在必應的首頁設置一下window.name
而後輸入location.href = 'http://www.baidu.com'
,跳轉到百度後再看一下name
666,window.name果真沒騙我,沒有變化。
接下來先貼代碼
父親窗口:
http://localhost:63342/test-field/cross-origin/local-test/name-parent.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <iframe id="frame" src="https://blank121.github.io/test-field/cross-origin/name.html"></iframe> <script> var iframe = document.getElementById('frame') var loaded = false iframe.onload = function () { if (!loaded) { loaded = true iframe.src = 'http://localhost:63342/test-field/cross-origin/local-test/name-proxy.html' } else { console.log(iframe.contentWindow.name) } } </script> </body> </html>
子窗口:
https://blank121.github.io/te...
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>window.name跨域</title> </head> <body> <script> window.name='今天,我要用window.name來實現跨域' </script> </body> </html>
代理窗口(啥也沒作。主要是要和父親窗口同源來傳遞name)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> </body> </html>
代碼如上,主要思路就是利用window.name不變性,子窗口設置了name以後,來加載一個和父親窗口同源的代理窗口,以此來獲取name(注意子窗口和代理窗口是同一個iframe,加載不一樣頁面而已,因此window.name不變)
首先了解一下postMessage
otherWindow.postMessage(message, targetOrigin);
otherWindow:指目標窗口,也就是給哪一個window發消息,是 window.frames 屬性的成員或者由 window.open 方法建立的窗口
message: 是要發送的消息,類型爲 String、Object (IE八、9 不支持)
targetOrigin: 是限定消息接收範圍,不限制請使用 *
postMessage是HTML5新特性,跨域簡直太方便了,就是兼容性要注意一下。
發送方頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <iframe id="frame" src="https://blank121.github.io/test-field/cross-origin/receive.html"> </iframe> <script> var iframe = document.getElementById('frame') iframe.onload = function () { iframe.contentWindow.postMessage("我今天就是要用postMessage跨域","https://blank121.github.io") } </script> </body> </html>
接收方頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script> function receiveMessage(event) { var p = document.createElement('p') p.innerText = event.data document.body.appendChild(p) } window.addEventListener("message", receiveMessage, false); </script> </body> </html>
接收方監聽一下message事件,就能夠接收到信息啦。
WebSocket 是一種在客戶端與服務器之間保持TCP長鏈接的網絡協議,這樣它們就能夠隨時進行信息交換(雙工通信)。
雖然任何客戶端或服務器上的應用均可以使用WebSocket,但原則上仍是指瀏覽器與服務器之間使用。經過WebSocket,服務器能夠直接向客戶端發送數據,而無須客戶端週期性的請求服務器,以動態更新數據內容。
WebSocket 很是強大,筆者在這方面也是小白級別的,之後有時間會詳細研究學習。
跨域代碼以下
頁面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> </body> </html> <script> // Create WebSocket connection. const socket = new WebSocket('ws://127.0.0.1:8080'); // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', event.data); }); </script>
服務端nodeJs代碼:
var WebSocketServer = require('ws').Server; var wss = new WebSocketServer({ port: 8080 }); wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); ws.send("hello"+message); }); });
結果如圖:
瀏覽器端:
服務端:
完美實現了跨域。
老生常談的CORS,優秀的文章已經很是多了,你們能夠搜一下,很是重要,有機會我會專門寫一篇文章來學習總結,在此就再也不詳述了
不得不說,這些方法仍是比較巧妙的,在此寫下一篇文章來總結一下,感受本身面對跨域絲絕不慌啦。