因爲瀏覽器的同源策略,若是兩個頁面的協議,端口(若是有指定)和域名都相同,則兩個頁面具備相同的源,只要有一者不一樣,就會形成跨域javascript
jsonp由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數。回調函數的名字通常是在請求中指定。而數據就是傳入回調函數中的JSON數據。形式以下:html
<script> var localHandle = function(data){ alert("返回的數據:" + data); } </script> <script src="http://remoteserver.com/remote.js?name=value&callback=localHandle"></script>
remote.js代碼以下:前端
localHandler({"result":"我是遠程js帶來的數據"});
服務器根據請求裏的callback=localHandle知道了本地的回調函數名稱localHandle,查詢字符串爲name=value,而後服務器執行相對應的函數並返回數據給本地的localHandle回調函數。java
建議動態的建立script來查詢node
<script> var localHandle = function(data){ alert("返回的數據:" + data); } // 提供jsonp服務的url地址(不論是什麼類型的地址,最終生成的返回值都是一段javascript代碼) var url = "http://remoteserver.com/remote.js?name=value&callback=localHandle"; // 建立script標籤,設置其屬性 var script = document.createElement('script'); script.setAttribute('src', url); // 把script標籤加入head,此時調用開始 document.getElementsByTagName('head')[0].appendChild(script); </script>
簡單易用,可以直接訪問響應文本,支持瀏覽器和服務器之間的雙向通訊json
/** * JSONP請求工具 * @param url 請求的地址 * @param data 請求的參數 * @returns {Promise<any>} */ const request = ({url, data}) => { return new Promise((resolve, reject) => { // 處理傳參成xx=yy&aa=bb的形式 const handleData = (data) => { const keys = Object.keys(data) const keysLen = keys.length return keys.reduce((pre, cur, index) => { const value = data[cur] const flag = index !== keysLen - 1 ? '&' : '' return `${pre}${cur}=${value}${flag}` }, '') } // 動態建立script標籤 const script = document.createElement('script') // 接口返回的數據獲取 window.jsonpCb = (res) => { document.body.removeChild(script) delete window.jsonpCb resolve(res) } script.src = `${url}?${handleData(data)}&cb=jsonpCb` document.body.appendChild(script) }) } // 使用方式 request({ url: 'http://localhost:9871/api/jsonp', data: { // 傳參 msg: 'helloJsonp' } }).then(res => { console.log(res) })
const requestPost = ({url, data}) => { // 首先建立一個用來發送數據的iframe. const iframe = document.createElement('iframe') iframe.name = 'iframePost' iframe.style.display = 'none' document.body.appendChild(iframe) const form = document.createElement('form') const node = document.createElement('input') // 註冊iframe的load事件處理程序,若是你須要在響應返回時執行一些操做的話. iframe.addEventListener('load', function () { console.log('post success') }) form.action = url // 在指定的iframe中執行form form.target = iframe.name; //target規定在何處打開 action URL。這裏能夠經過iframe.name來指定iframe form.method = 'post' for (let name in data) { node.name = name node.value = data[name].toString() form.appendChild(node.cloneNode()) } // 表單元素須要添加到主文檔中. form.style.display = 'none' document.body.appendChild(form) form.submit() // 表單提交後,就能夠刪除這個表單,不影響下次的數據發送. document.body.removeChild(form) } // 使用方式 requestPost({ url: 'http://localhost:9871/api/iframePost', data: { msg: 'helloIframePost' } })
經過document.domain將兩個頁面的域名都設置成相同域名,也能夠實現跨域。 不過這個方法只適用於不一樣子域的框架間的交互api
// a.html <iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script>
// b.html <script> document.domain = 'domain.com'; // 獲取父窗口中變量 alert('get js data from parent ---> ' + window.parent.user); </script>
經過location.hash來跨域,原理是改變url的hash部分來進行雙向通訊,父頁面向iframe子頁面通訊只需監聽自身的url變化來發送信息,而子頁面向父頁面通訊就麻煩一些,因爲兩個頁面不在同一個域,IE和Chrome都不容許修改parent.location.hash的值,因此須要建立一個和父頁面同域的中間頁,中間頁可利用parent.parent訪問父頁面的全部對象。跨域
該方法的缺點是會形成沒必要要的瀏覽器歷史記錄,而且有些瀏覽器不支持onhashchange事件,數據直接暴露在url中,數據容量和類型都有限等。瀏覽器
// a.html <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 向b.html傳hash值 setTimeout(function() { iframe.src = iframe.src + '#user=admin'; }, 1000); // 開放給同域c.html的回調方法 function onCallback(res) { alert('data from c.html ---> ' + res); } </script>
// b.html <iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 監聽a.html傳來的hash值,再傳給c.html window.onhashchange = function () { iframe.src = iframe.src + location.hash; }; </script>
// c.html <script> // 監聽b.html傳來的hash值 window.onhashchange = function () { // 再經過操做同域a.html的js回調,將結果傳回 window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', '')); }; </script>
window.name + iframe,利用window.name在不一樣頁面(甚至不一樣域)加載後依舊存在,而且支持大小達到了2MB。 步驟: 首先在a頁面中建立iframe,將src指向外域保存數據到window.name,再將iframe的src指向和a頁面同域的b代理頁面,藉此讓a頁面以iframe.contentWindow.name的形式成功讀取數據。安全
//a.html var proxy = function(url, callback) { var state = 0; var iframe = document.createElement('iframe'); // 加載跨域頁面 iframe.src = url; // onload事件會觸發2次,第1次加載跨域頁,並留存數據於window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy頁)成功後,讀取同域window.name中數據 callback(iframe.contentWindow.name); destoryFrame(); } else if (state === 0) { // 第1次onload(跨域頁)成功後,切換到同域代理頁面 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html'; state = 1; } }; document.body.appendChild(iframe); // 獲取數據之後銷燬這個iframe,釋放內存;這也保證了安全(不被其餘域frame js訪問) function destoryFrame() { iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } }; // 請求跨域b頁面數據 proxy('http://www.domain2.com/b.html', function(data){ alert(data); });
//b.html <script> window.name = 'This is domain2 data!'; </script>
postMessage是HTML5新增的window屬性
用法:postMessage(data,origin)方法接受兩個參數
//a.html <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2傳送跨域數據 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回數據 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script>
//b.html <script> // 接收domain1的數據 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 處理後再發回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
CORS須要瀏覽器和服務器同時支持。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
整個CORS通訊過程當中,都是瀏覽器自動完成,對於開發者來講,CORS和同源的AJAX通訊沒有差異,瀏覽器一旦發現AJAX跨域,就會自動添加一些附加的頭部信息,根據請求的不一樣還會多出一次附加的請求。
實現CORS通訊的關鍵是服務器,只要服務器實現了CORS接口,就實現了跨域
服務器實現CORS通訊的關鍵是設置如下請求頭
Access-Control-Allow-Origin: * //容許全部域 Access-Control-Allow-Origin: <origin> 容許指定的origin
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
CORS與JSONP的使用目的相同,可是比JSONP更強大。
JSONP只支持GET請求,CORS支持全部類型的HTTP請求。JSONP的優點在於支持老式瀏覽器,以及能夠向不支持CORS的網站請求數據。