再談前端跨域

1. JSONP

首先要介紹的跨域方法必然是 JSONP。
現(xiàn)在你想要獲取其他網(wǎng)站上的 JavaScript 腳本,你非常高興的使XMLHttpRequest 對(duì)象來(lái)獲取。但是瀏覽器一點(diǎn)兒也不配合你,無(wú)情的彈出了下面的錯(cuò)誤信息:

XMLHttpRequest cannot load http://x.com/main.dat. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://y.com' is therefore not allowed access.

你心里肯定會(huì)想,我難道要用后臺(tái)做個(gè)爬蟲(chóng)來(lái)獲取這個(gè)數(shù)據(jù)嗎?!為了避免這種事情發(fā)生,JSONP 就派上用場(chǎng)了。
<script>標(biāo)簽是不受同源策略的限制的,它可以載入任意地方的 JavaScript 文件,而并不要求同源。所以 JSONP 的理念就是,我和服務(wù)端約定好一個(gè)函數(shù)名,當(dāng)我請(qǐng)求文件的時(shí)候,服務(wù)端返回一段 JavaScript。這段 JavaScript 調(diào)用了我們約定好的函數(shù),并且將數(shù)據(jù)當(dāng)做參數(shù)傳入。非常巧合的一點(diǎn)(其實(shí)并不是),JSON 的數(shù)據(jù)格式和JavaScript 語(yǔ)言里對(duì)象的格式正好相同。所以在我們約定的函數(shù)里面可以直接使用這個(gè)對(duì)象。光說(shuō)不練假把式,讓我們來(lái)看一個(gè)例子:
你需要獲取數(shù)據(jù)的頁(yè)面 index.html:

<script>   
  function getWeather(data) {       
    console.log(data);
  }
</script>
<script src="http://x.y.com/xx.js">

http://x.y.com/xx.js 文件內(nèi)容:

getWeather({
  "城市": "北京",
  "天氣": "大霧"
});

我們可以看到,在我們定義了 getWeather(data)這個(gè)函數(shù)后,直接載入了 xx.js。在這個(gè)腳本中,執(zhí)行了 getWeather函數(shù),并傳入了一個(gè)對(duì)象。然后我們?cè)谶@個(gè)函數(shù)中將這個(gè)對(duì)象輸出到 console 中。

這就是整個(gè) JSONP 的流程。

2. document.domain

使用條件:

  1. 有其他頁(yè)面 window對(duì)象的引用
  2. 二級(jí)域名相同
  3. 協(xié)議相同
  4. 端口相同

document.domain默認(rèn)的值是整個(gè)域名,所以即使兩個(gè)域名的二級(jí)域名一樣,那么他們的 document.domain也不一樣。使用方法就是將符合上述條件頁(yè)面的 document.domain設(shè)置為同樣的二級(jí)域名。這樣我們就可以使用其他頁(yè)面的 window對(duì)象引用做我們想做的任何事情了。
補(bǔ)充知識(shí):
x.one.example.comy.one.example.com 可以將 document.domain設(shè)置為 one.example.com,也可以設(shè)置為example.com。document.domain只能設(shè)置為當(dāng)前域名的一個(gè)后綴,并且包括二級(jí)域名或以上(.edu.cn這種整個(gè)算頂級(jí)域名)。我們直接操刀演示,用兩個(gè)網(wǎng)站 http://wenku.baidu.com/http://zhidao.baidu.com/。這兩個(gè)網(wǎng)站都是 http 協(xié)議,端口都是 80, 且二級(jí)域名都是 baidu.com。打開(kāi)http://wenku.baidu.com/,在 console 中輸入代碼:

document.domain = 'baidu.com';
var otherWindow = window.open('http://zhidao.baidu.com/');

我們現(xiàn)在已經(jīng)發(fā)現(xiàn)百度知道的網(wǎng)頁(yè)已經(jīng)打開(kāi)了,在百度知道網(wǎng)頁(yè)的 console 中輸入以下代碼:

document.domain = 'baidu.com';

現(xiàn)在回到百度文庫(kù)的網(wǎng)頁(yè),我們就可以使用百度知道網(wǎng)頁(yè)的 window對(duì)象來(lái)操作百度知道的網(wǎng)頁(yè)了。例如:

var divs = otherWindow.document.getElementsByTagName('div');

上面這個(gè)例子的使用方法并不常見(jiàn),但是非常詳細(xì)的說(shuō)明了這種方法的原理。這種方法主要用在控制 <iframe>的情況中。
比如我的頁(yè)面(http://one.example.com/index.html)中內(nèi)嵌了一個(gè) <iframe>:

<iframe id="iframe" src="http://two.example.com/iframe.html"></iframe>

我們?cè)?iframe.html 中使用 JavaScript 將 document.domain設(shè)置好,也就是 example.com。在 index.html 執(zhí)行以下腳本:

var iframe = document.getElementById('iframe');
document.domain = 'example.com';
iframe.contentDocument; // iframe的 document 對(duì)象
iframe.contentWindow; // iframe的 window 對(duì)象

這樣,我們就可以獲得對(duì)iframe的完全控制權(quán)了。

補(bǔ)充知識(shí):
當(dāng)兩個(gè)頁(yè)面不做任何處理,但是使用了iframe或者 window.open() 得到了某個(gè)頁(yè)面的 window 對(duì)象的引用,我們可以直接訪問(wèn)的屬性有哪些?

方法
window.blur
window.close
window.focus
window.postMessage
window.location.replace
屬性 權(quán)限
window.closed 只讀
window.frames 只讀
window.length 只讀
window.location.href 只寫
window.opener 只讀
window.parent 只讀
window.self 只讀
window.top 只讀
window.window 只讀

3. window.name

我們來(lái)看以下一個(gè)場(chǎng)景:
隨意打開(kāi)一個(gè)頁(yè)面,輸入以下代碼:

window.name = "My window's name";
location.;

再檢測(cè) window.name :

window.name; // My window's name

可以看到,如果在一個(gè)標(biāo)簽里面跳轉(zhuǎn)網(wǎng)頁(yè)的話,我們的 window.name是不會(huì)改變的?;谶@個(gè)思想,我們可以在某個(gè)頁(yè)面設(shè)置好 window.name的值,然后跳轉(zhuǎn)到另外一個(gè)頁(yè)面。在這個(gè)頁(yè)面中就可以獲取到我們剛剛設(shè)置的 window.name了。由于安全原因,瀏覽器始終會(huì)保持 window.name 是 string類型。這個(gè)方法也可以應(yīng)用到與 <iframe> 的交互上來(lái)。我的頁(yè)面(http://one.example.com/index.html)中內(nèi)嵌了一個(gè) <iframe>:

<iframe id="iframe" src="http://omg.com/iframe.html"></iframe>

在 iframe.html 中設(shè)置好了 window.name為我們要傳遞的字符串。我們?cè)?index.html 中寫了下面的代碼:

var iframe = document.getElementById('iframe');
var data = '';
iframe.onload = function() {   
  data = iframe.contentWindow.name;
};

定睛一看,為毛線報(bào)錯(cuò)?細(xì)心的讀者們肯定已經(jīng)發(fā)現(xiàn)了,兩個(gè)頁(yè)面完全不同源啊!由于 window.name 不隨著 URL 的跳轉(zhuǎn)而改變,所以我們使用一個(gè)暗黑技術(shù)來(lái)解決這個(gè)問(wèn)題:

var iframe = document.getElementById('iframe');
var data = '';
iframe.onload = function() {    
  iframe.onload = function() {        
    data = iframe.contentWindow.name;    
  }   
   iframe.src = 'about:blank';
};

或者將里面的 about:blank 替換成某個(gè)同源頁(yè)面(最好是空頁(yè)面,減少加載時(shí)間)。
補(bǔ)充知識(shí):
about:blank,javascript: 和 data:中的內(nèi)容,繼承了載入他們的頁(yè)面的源。
這種方法與 document.domain方法相比,放寬了域名后綴要相同的限制,可以從任意頁(yè)面獲取 string類型的數(shù)據(jù)。

4. [HTML5] postMessage

在 HTML5 中, window 對(duì)象增加了一個(gè)非常有用的方法:

windowObj.postMessage(message, targetOrigin);

windowObj: 接受消息的 Window 對(duì)象。
message: 在最新的瀏覽器中可以是對(duì)象。
targetOrigin: 目標(biāo)的源,* 表示任意。

這個(gè)方法非常強(qiáng)大,無(wú)視協(xié)議,端口,域名的不同。
下面是烤熟的栗子:

var windowObj = window; // 可以是其他的 Window 對(duì)象的引用
var data = null;
addEventListener('message', function(e) {   
  if(e.origin == 'http://qiaohongshen.github.io/foo') {      
    data = e.data;
    e.source.postMessage('Got it!', '*');
  }
});

message事件就是用來(lái)接收 postMessage發(fā)送過(guò)來(lái)的請(qǐng)求的。函數(shù)參數(shù)的屬性有以下幾個(gè):
origin: 發(fā)送消息的 window的源。
data: 數(shù)據(jù)。
source: 發(fā)送消息的 Window對(duì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 什么是跨域? 跨域一詞從字面意思看,就是跨域名嘛,但實(shí)際上跨域的范圍絕對(duì)不止那么狹隘。具體概念如下:只要協(xié)議...
    w_zhuan閱讀 620評(píng)論 0 0
  • 1. 什么是跨域? 跨域一詞從字面意思看,就是跨域名嘛,但實(shí)際上跨域的范圍絕對(duì)不止那么狹隘。具體概念如下:只要協(xié)議...
    他在發(fā)呆閱讀 858評(píng)論 0 0
  • 跨域資源共享 CORS 對(duì)于web開(kāi)發(fā)來(lái)講,由于瀏覽器的同源策略,我們需要經(jīng)常使用一些hack的方法去跨域獲取資源...
    默默先生Alec閱讀 665評(píng)論 0 0
  • 來(lái)吧,少年,今天還能看文章學(xué)習(xí)的,一多半都是單身貴族,看朋友圈還會(huì)被虐,不如學(xué)習(xí),上街還會(huì)被虐,不如學(xué)習(xí),痛并快樂(lè)...
    范小飯_閱讀 8,166評(píng)論 3 24
  • 這是信息密集轟炸的一周,原本我只想喝口水,卻不料打開(kāi)的是消防栓! 既然開(kāi)啟了新的征程,自己選的,定是要遵從內(nèi)心走下...
    成功的種子閱讀 249評(píng)論 2 2

友情鏈接更多精彩內(nèi)容