瀏覽器異步加載和同源策略

靜態(tài)頁(yè)面

在瀏覽器腳本的概念沒(méi)有出現(xiàn)之前,所有的網(wǎng)頁(yè)都是靜態(tài)的卷哩。我們知道瀏覽器的工作模式是:

  1. 瀏覽器向網(wǎng)站服務(wù)器發(fā)起請(qǐng)求
  2. 網(wǎng)站接受瀏覽器的請(qǐng)求吸祟,返回一些字符串(比如一些組成頁(yè)面的 HTML 字符串)
  3. 瀏覽器接收到網(wǎng)站返回的用于組成頁(yè)面的字符串后匙头,就可以關(guān)閉連接了
  4. 瀏覽器將組成頁(yè)面的字符串渲染到屏幕上漫谷,使得用戶可以看到一個(gè)可視化的結(jié)果

看起來(lái)就像下面這樣:

                                                                                  ?
                                       Client Request                              
              +-------------+                                     +--------+       
+------+      |  User Agent | +-------------------------------->  |        |       
| User +------>             |                                     | Server |       
+--^---+      |  (Browser)  | <--------------------------------+  |        |       
   |          +-------+-----+                                     +--------+       
   |                  |                Server Response                             
   |                  |                                                            
   |                  |                                                            
   |        +---------v--------+                                                   
   |        | Close Connection |                                                   
   |        +---------+--------+                                                   
   |                  |                                                            
   |                  |                                                            
   |         +--------v--------+                                                   
   ^---------+ Render response |                                                   
             +-----------------+                                                   

我們看到,一旦用戶代理(瀏覽器)關(guān)閉了和服務(wù)器之間的鏈接之后蹂析,客戶端和服務(wù)器之間將不能繼續(xù)通信舔示。

動(dòng)態(tài)頁(yè)面

為了讓頁(yè)面可以給用戶帶來(lái)更多的交互,瀏覽器開(kāi)發(fā)廠商們制造出了名為瀏覽器腳本的東西识窿。比如你在瀏覽一個(gè)頁(yè)面的時(shí)候斩郎,你覺(jué)得頁(yè)面的字體太小了。在靜態(tài)頁(yè)面的時(shí)候喻频,頁(yè)面制作者在右上角給你提供了名為 “放大字體” 的按鈕缩宜,你點(diǎn)擊那個(gè)按鈕,然后開(kāi)啟一輪新的請(qǐng)求甥温,顯著的說(shuō)就是說(shuō)你感覺(jué)到瀏覽器刷新了锻煌。這其實(shí)是瀏覽器重新從服務(wù)器加載頁(yè)面的資源,只不過(guò)這一次的資源是用于顯示字體放大后的頁(yè)面姻蚓。

瀏覽器腳本就是一小段由瀏覽器執(zhí)行的代碼宋梧,頁(yè)面制作者將這一小段代碼,和網(wǎng)頁(yè)面的內(nèi)容(比如一篇優(yōu)美的散文狰挡,和它右上角的 “放大字體” 按鈕)一起返回給瀏覽器捂龄。瀏覽器接收到頁(yè)面資源后,首先就是先將散文和 “放大字體” 按鈕顯示出來(lái)加叁。注意到返回的內(nèi)容實(shí)際上還有一段由瀏覽器執(zhí)行的代碼倦沧,頁(yè)面制作者在這段帶代碼中告訴瀏覽器:如果用戶點(diǎn)擊了 “放大字體” 按鈕,那么你就將頁(yè)面的字體放大它匕。于是展融,當(dāng)你點(diǎn)擊 “放大字體” 按鈕之后,瀏覽器嚴(yán)格執(zhí)行頁(yè)面制作者在腳本中撰寫的內(nèi)容 - 將頁(yè)面的字體放大豫柬。

異步加載

注意在靜態(tài)頁(yè)面中瀏覽器和服務(wù)器之間的通信過(guò)程告希。瀏覽器在向服務(wù)器發(fā)起了對(duì)頁(yè)面的請(qǐng)求之后扑浸,在服務(wù)器沒(méi)有將頁(yè)面的內(nèi)容返回之前,頁(yè)面是無(wú)法被顯示出來(lái)的燕偶,最顯著的特征就是我們?cè)邳c(diǎn)擊了瀏覽器的 “刷新” 按鈕之后喝噪,頁(yè)面會(huì) “白屏” 一小段時(shí)間。

起初瀏覽器腳本是沒(méi)有網(wǎng)絡(luò)通信的功能的指么,只能做一些頁(yè)面的特效仙逻,比如“點(diǎn)擊按鈕放大了字體”。不過(guò)瀏覽器廠商發(fā)現(xiàn)涧尿,如果給腳本賦予網(wǎng)絡(luò)通信的功能,將使得頁(yè)面制作者可以給用戶提供更好的頁(yè)面交互體驗(yàn)檬贰。于是在早期的 IE 瀏覽器中姑廉,首先賦予了瀏覽器腳本的通信功能。

瀏覽器腳本可以和服務(wù)器進(jìn)行網(wǎng)絡(luò)通信之后翁涤,頁(yè)面制作者可以做出具有更好體驗(yàn)的頁(yè)面桥言。比如你現(xiàn)在需要搜索商品,假設(shè)是要買一本編程的書葵礼,你在網(wǎng)頁(yè)的搜索框中輸入了 “編程的數(shù)”号阿,很明顯是輸錯(cuò)了,你將 “書” 錯(cuò)輸成了 “數(shù)”鸳粉。在你點(diǎn)擊了 “搜索” 按鈕之后扔涧,進(jìn)過(guò)短暫的白屏之后,頁(yè)面中顯示了:

找不到關(guān)于 “編程的數(shù)” 的產(chǎn)品届谈,你是不是要找 “編程的書”

很不錯(cuò)枯夜,網(wǎng)站給了我們一個(gè)提示,這樣我們就可以發(fā)現(xiàn)自己的輸入錯(cuò)誤艰山。不過(guò)這個(gè)體驗(yàn)還是有待提高的湖雹,因?yàn)槊恳淮蔚乃阉鞫紩?huì)有一個(gè)短暫的 “白屏”,在白屏期間用戶只能等待曙搬。在瀏覽器腳本可以通信之后摔吏,搜索就可以以一個(gè)異步的方式進(jìn)行:

  1. 用戶在瀏覽器中輸入搜索頁(yè)面的地址 “http://search.shop.com
  2. 瀏覽器會(huì)向網(wǎng)站請(qǐng)求搜索頁(yè)面的內(nèi)容,用于顯示這個(gè)頁(yè)面
  3. 網(wǎng)站在返回頁(yè)面的顯示內(nèi)容的同時(shí)纵装,包含了一小段腳本征讲,腳本的內(nèi)容是告訴瀏覽器 “用戶在點(diǎn)擊了搜索之后,你給用戶一個(gè)提示搂擦,讓用戶知道服務(wù)器正在緊張的搜索用戶所需的資源稳诚,然后你顯示了提示后,你再向服務(wù)器請(qǐng)求搜索的結(jié)果瀑踢,當(dāng)?shù)玫剿阉鹘Y(jié)果后扳还,你再把搜索結(jié)果顯示給用戶”

這樣的話才避,用戶不必在搜索時(shí)面對(duì)頁(yè)面的刷新時(shí)的 “白屏” 了,有一個(gè)提示框告訴用戶稍等片刻氨距。

同源策略

為了定位網(wǎng)絡(luò)上的資源桑逝,我們采用了統(tǒng)一資源定位符 URL,就像是一個(gè)門牌號(hào)一樣俏让, URL 標(biāo)識(shí)出資源在網(wǎng)絡(luò)上的位置楞遏。我們?yōu)g覽的網(wǎng)頁(yè),其中的內(nèi)容可能會(huì)來(lái)自不同的提供者首昔,比如散文來(lái)自一位作家寡喝,而其中的配圖來(lái)自一位美術(shù)家。散文的 URL 是 http://writer.com/new-world勒奇,配圖的 URL 是 http://artist.com/new-world预鬓。

我們需要有一種方式將網(wǎng)絡(luò)上的資源(比如散文和圖畫)標(biāo)識(shí)出來(lái),區(qū)別它們是來(lái)自于不同的作者赊颠。如果我們將顆粒度定位到每一個(gè)獨(dú)立的資源格二,理論上是可行的,但是我們知道作家不可能只有一篇散文竣蹦,而美術(shù)家也不會(huì)只有一幅畫顶猜。于是我們選擇了使用:通信協(xié)議,完整的域名痘括,以及端口號(hào)去描述一個(gè)源长窄,只有三者都相同,才標(biāo)識(shí)兩個(gè)資源是同源的纲菌。

下面的幾個(gè)資源是同源的:

http://example.com/ 
http://example.com:80/ 
http://example.com/path/file

下面的資源是不同源的:

http://example.com/ 
http://example.com:8080/ 
http://www.example.com/ 
https://example.com:80/ 
https://example.com/ 
http://example.org/ 
http://ietf.org/

現(xiàn)在知道了同源抄淑,那么同源策略是什么意思呢?同源策略就是驰后,兩個(gè)不同源的資源相互是不能訪問(wèn)對(duì)方的資源的肆资。同源策略主要就是限制腳本的網(wǎng)絡(luò)訪問(wèn)。

比如我們打開(kāi)了一個(gè)頁(yè)面 http://example.com灶芝,這個(gè)頁(yè)面有兩段腳本郑原,一個(gè)段使用的內(nèi)聯(lián)的方式稱為 A,它主要就是在用戶點(diǎn)擊了按鈕之后顯示一段文字夜涕,告訴用戶點(diǎn)擊了按鈕犯犁;另一段作為外部資源進(jìn)行加載稱為 B,B 是 A 的基礎(chǔ)代碼女器,比如 B 是 jQuery酸役,它被放在了 http://cdn.jquery.com 上。首先我們知道,這兩段代碼如果按照同源的定義涣澡,肯定是不同源的贱呐。也就是說(shuō)我們?cè)?http://example.com 的頁(yè)面上是不能加載 http://cdn.jquery.com 上的資源的。

好像與現(xiàn)實(shí)情況有點(diǎn)矛盾入桂。之所以現(xiàn)在可以奄薇,是因?yàn)闉g覽器為了適應(yīng)實(shí)際的生產(chǎn)情況,放寬了對(duì)同源策略的檢查抗愁,因?yàn)槲覀冎滥俚伲豢赡軐⑺械馁Y源都放在同一臺(tái)機(jī)器上。那么在頁(yè)面完全加載好之后蜘腌,頁(yè)面中的腳本(內(nèi)聯(lián)的和外部引入)的都被瀏覽器歸納到了和當(dāng)前頁(yè)面相同的源沫屡,都屬于 http://example.com 了。這么做的意思就是撮珠,腳本無(wú)法訪問(wèn)與之不同源的資源谁鳍,也就是此時(shí)的腳本(內(nèi)聯(lián)的和外部引入的)無(wú)法訪問(wèn)資源 https://example.com/user-info

繞過(guò)同源策略

有時(shí)比如上面的例子劫瞳,我們確實(shí)需要在腳本中加載和當(dāng)前頁(yè)面不同源的資源,比如在 http://example.com 頁(yè)面中使用腳本加載 https://example.com/user-info 中的內(nèi)容绷柒。那么如何繞過(guò)瀏覽器的同源策略呢志于?

我們知道直接在頁(yè)面中載入不同源的外部資源是可以的,那么我們就可以動(dòng)態(tài)的載入一段外部的腳本废睦。

首先伺绽,我們的 http://example.com 中有這么一段腳本:

(function () {
    window['showNickname'] = function (json) {
        alert(json['nickname']);
    };

    var userInfoServiceUrl = 'https://example.com/user-info';

    var doCrossSiteRequest = function (url, callback) {
        var script = document.createElement('script');
        script.src = url + '?callback=' + callback;
        var head = document.getElementsByTagName('head');
        if (head[0]) {
            head.append(script);
        }
    };

    document.querySelector('#btnShowNickName').addEventListener('click', function () {
        doCrossSiteRequest(userInfoServiceUrl, 'showNickname');
    });
})();

https://example.com/user-info 的服務(wù)端內(nèi)容為:

<?php

$callback = isset($_GET['callback']) ? $_GET['callback'] : null;
if ($callback === null) die('invalid request');

$userInfo = [
    'nickname' => 'net-user'
];
$json = json_encode($userInfo);

echo "{$callback}({$json});";

那么在瀏覽器加載了 https://example.com/user-info 的腳本為,得到的是:

showNickname({"nickname":"net-user"});

這就和我們最先在 http://example.com 留下的 window['showNickname'] 對(duì)接上了嗜湃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奈应,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子购披,更是在濱河造成了極大的恐慌杖挣,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刚陡,死亡現(xiàn)場(chǎng)離奇詭異惩妇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)筐乳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門歌殃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蝙云,你說(shuō)我怎么就攤上這事氓皱。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵波材,是天一觀的道長(zhǎng)股淡。 經(jīng)常有香客問(wèn)我,道長(zhǎng)各聘,這世上最難降的妖魔是什么揣非? 我笑而不...
    開(kāi)封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮躲因,結(jié)果婚禮上早敬,老公的妹妹穿的比我還像新娘。我一直安慰自己大脉,他們只是感情好搞监,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著镰矿,像睡著了一般琐驴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秤标,一...
    開(kāi)封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天绝淡,我揣著相機(jī)與錄音,去河邊找鬼苍姜。 笑死牢酵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衙猪。 我是一名探鬼主播馍乙,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼垫释!你這毒婦竟也來(lái)了丝格?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤棵譬,失蹤者是張志新(化名)和其女友劉穎显蝌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體订咸,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琅束,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了算谈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩禀。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖然眼,靈堂內(nèi)的尸體忽然破棺而出艾船,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布屿岂,位于F島的核電站践宴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏爷怀。R本人自食惡果不足惜阻肩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望运授。 院中可真熱鬧烤惊,春花似錦、人聲如沸吁朦。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逗宜。三九已至雄右,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纺讲,已是汗流浹背擂仍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熬甚,地道東北人逢渔。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像则涯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冲簿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容