天天看點

全面分析前端的網絡請求方式

摘要: 發個請求也不簡單啊。

  • 原文:全面分析前端的網絡請求方式
  • 作者:ConardLi

Fundebug經授權轉載,版權歸原作者所有。

一、前端進行網絡請求的關注點

大多數情況下,在前端發起一個網絡請求我們隻需關注下面幾點:

  • 傳入基本參數(

    url

    ,請求方式)
  • 請求參數、請求參數類型
  • 設定請求頭
  • 擷取響應的方式
  • 擷取響應頭、響應狀态、響應結果
  • 異常處理
  • 攜帶

    cookie

    設定
  • 跨域請求

二、前端進行網絡請求的方式

  • form

    表單、

    ifream

    、重新整理頁面
  • Ajax

    - 異步網絡請求的開山鼻祖
  • jQuery

    - 一個時代
  • fetch

    -

    Ajax

    的替代者
  • axios、request

    等衆多開源庫

三、關于網絡請求的疑問

  • Ajax

    的出現解決了什麼問題
  • 原生

    Ajax

    如何使用
  • jQuery

    的網絡請求方式
  • fetch

    的用法以及坑點
  • 如何正确的使用

    fetch

  • 如何選擇合适的跨域方式

帶着以上這些問題、關注點我們對幾種網絡請求進行一次全面的分析。

四、Ajax 的出現解決了什麼問題

Ajax

出現之前,

web

程式是這樣工作的:

全面分析前端的網絡請求方式

這種互動的的缺陷是顯而易見的,任何和伺服器的互動都需要重新整理頁面,使用者體驗非常差,

Ajax

的出現解決了這個問題。

Ajax

全稱

Asynchronous JavaScript + XML

(異步

JavaScript

XML

使用

Ajax

,網頁應用能夠快速地将增量更新呈現在使用者界面上,而不需要重載(重新整理)整個頁面。

Ajax

本身不是一種新技術,而是用來描述一種使用現有技術集合實作的一個技術方案,浏覽器的

XMLHttpRequest

是實作

Ajax

最重要的對象(

IE6

以下使用

ActiveXObject

)。

盡管

X

Ajax

中代表

XML

, 但由于

JSON

的許多優勢,比如更加輕量以及作為

Javascript

的一部分,目前

JSON

的使用比

XML

更加普遍。

五、原生 Ajax 的用法

這裡主要分析

XMLHttpRequest

對象,下面是它的一段基礎使用:

var xhr = new XMLHttpRequest();
xhr.open(\'post\',\'www.xxx.com\',true)
// 接收傳回值
xhr.onreadystatechange = function(){
    if(xhr.readyState === 4 ){
        if(xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            console.log(xhr.responseText);
        }
    }
}
// 處理請求參數
postData = {"name1":"value1","name2":"value2"};
postData = (function(value){
var dataString = "";
for(var key in value){
     dataString += key+"="+value[key]+"&";
};
  return dataString;
}(postData));
// 設定請求頭
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// 異常處理
xhr.onerror = function() {
   console.log(\'Network request failed\')
}
// 跨域攜帶cookie
xhr.withCredentials = true;
// 送出請求
xhr.send(postData);
           

下面分别對

XMLHttpRequest

對象常用的的函數、屬性、事件進行分析。

全面分析前端的網絡請求方式

函數

open

用于初始化一個請求,用法:

xhr.open(method, url, async);
           
  • method

    :請求方式,如

    get、post

  • url

    :請求的

    url

  • async

    :是否為異步請求

send

用于發送

HTTP

請求,即調用該方法後

HTTP

請求才會被真正發出,用法:

xhr.send(param);
           
  • param

    :http 請求的參數,可以為

    string、Blob

    等類型。

abort

用于終止一個

ajax

請求,調用此方法後

readyState

将被設定為

,用法:

xhr.abort();
           

setRequestHeader

用于設定

HTTP

請求頭,此方法必須在

open()

方法和

send()

之間調用,用法:

xhr.setRequestHeader(header, value);
           

getResponseHeader

用于擷取

http

傳回頭,如果在傳回頭中有多個一樣的名稱,那麼傳回的值就會是用逗号和空格将值分隔的字元串,用法:

var header = xhr.getResponseHeader(name);
           

屬性

readyState

用來辨別目前

XMLHttpRequest

對象所處的狀态,

XMLHttpRequest

對象總是位于下列狀态中的一個:

狀态 描述

UNSENT

代理被建立,但尚未調用

open()

方法。
1

OPENED

open()

方法已經被調用。
2

HEADERS_RECEIVED

send()

方法已經被調用,并且頭部和狀态已經可獲得。
3

LOADING

下載下傳中;

responseText

屬性已經包含部分資料。
4

DONE

下載下傳操作已完成。

status

表示

http

請求的狀态, 初始值為

。如果伺服器沒有顯式地指定狀态碼, 那麼

status

将被設定為預設值, 即

200

responseType

表示響應的資料類型,并允許我們手動設定,如果為空,預設為

text

類型,可以有下面的取值:

描述

""

responseType

設為空字元串與設定為

"text"

相同, 是預設類型 (實際上是

DOMString

)。

"arraybuffer"

response

是一個包含二進制資料的

JavaScript ArrayBuffer

"blob"

response

是一個包含二進制資料的

Blob

對象 。

"document"

response 是一個

HTML Document

XML XMLDocument

,這取決于接收到的資料的 MIME 類型。

"json"

response

是一個 JavaScript 對象。這個對象是通過将接收到的資料類型視為

JSON

解析得到的。

"text"

response

是包含在

DOMString

對象中的文本。

response

傳回響應的正文,傳回的類型由上面的

responseType

決定。

withCredentials

ajax

請求預設會攜帶同源請求的

cookie

,而跨域請求則不會攜帶

cookie

,設定

xhr

withCredentials

的屬性為

true

将允許攜帶跨域

cookie

事件回調

onreadystatechange

xhr.onreadystatechange = callback;
           

readyState

屬性發生變化時,callback 會被觸發。

onloadstart

xhr.onloadstart = callback;
           

ajax

請求發送之前(

readyState==1

後,

readyState==2

前),

callback

會被觸發。

onprogress

xhr.onprogress = function(event) {
    console.log(event.loaded / event.total);
};

           

回調函數可以擷取資源總大小

total

,已經加載的資源大小

loaded

,用這兩個值可以計算加載進度。

onload

xhr.onload = callback;

           

當一個資源及其依賴資源已完成加載時,将觸發

callback

,通常我們會在

onload

事件中處理傳回值。

異常處理

onerror

xhr.onerror = callback;

           

ajax

資源加載失敗時會觸發

callback

ontimeout

xhr.ontimeout = callback;

           

當進度由于預定時間到期而終止時,會觸發

callback

,逾時時間可使用

timeout

屬性進行設定。

推薦大家使用Fundebug,一款很好用的BUG監控工具~

六、jQuery 對 Ajax 的封裝

在很長一段時間裡,人們使用

jQuery

提供的

ajax

封裝進行網絡請求,包括

$.ajax、$.get、$.post

等,這幾個方法放到現在,我依然覺得很實用。

$.ajax({
    dataType: \'json\', // 設定傳回值類型
    contentType: \'application/json\', // 設定參數類型
    headers: {\'Content-Type\',\'application/json\'},// 設定請求頭
    xhrFields: { withCredentials: true }, // 跨域攜帶cookie
    data: JSON.stringify({a: [{b:1, a:1}]}), // 傳遞參數
    error:function(xhr,status){  // 錯誤處理
       console.log(xhr,status);
    },
    success: function (data,status) {  // 擷取結果
       console.log(data,status);
    }
})

           

$.ajax

隻接收一個參數,這個參數接收一系列配置,其自己封裝了一個

jqXHR

對象,有興趣可以閱讀一下jQuary-ajax 源碼

全面分析前端的網絡請求方式

常用配置:

url

目前頁位址。發送請求的位址。

type

類型:

String

請求方式 (

"POST"

"GET"

), 預設為

"GET"

。注意:其它

HTTP

請求方法,如

PUT

DELETE

也可以使用,但僅部分浏覽器支援。

timeout

類型:

Number

設定請求逾時時間(毫秒)。此設定将覆寫全局設定。

success

類型:

Function

請求成功後的回調函數。

jsonp

在一個

jsonp

請求中重寫回調函數的名字。這個值用來替代在

"callback=?"

這種

GET

POST

請求中

URL

參數裡的

"callback"

部分。

error 類型:

Function

。請求失敗時調用此函數。

注意:源碼裡對錯誤的判定:

isSuccess = (status >= 200 && status < 300) || status === 304;

           

傳回值除了這幾個狀态碼都會進

error

回調。

dataType

"xml": 傳回 XML 文檔,可用 jQuery 處理。
"html": 傳回純文字 HTML 資訊;包含的 script 标簽會在插入 dom 時執行。
"script": 傳回純文字 JavaScript 代碼。不會自動緩存結果。除非設定了 "cache" 參數。注意:在遠端請求時(不在同一個域下),所有 POST 請求都将轉為 GET 請求。(因為将使用 DOM 的 script标簽來加載)
"json": 傳回 JSON 資料 。
"jsonp": JSONP 格式。使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 将自動替換 ? 為正确的函數名,以執行回調函數。
"text": 傳回純文字字元串

           

data

類型:

String

使用

JSON.stringify

轉碼

complete

類型:

Function

請求完成後回調函數 (請求成功或失敗之後均調用)。

async

類型:

Boolean

預設值:

true

。預設設定下,所有請求均為異步請求。如果需要發送同步請求,請将此選項設定為

false

contentType

類型:

String

預設值:

"application/x-www-form-urlencoded"

。發送資訊至伺服器時内容編碼類型。

鍵值對這樣組織在一般的情況下是沒有什麼問題的,這裡說的一般是,不帶嵌套類型

JSON

,也就是 簡單的

JSON

,形如這樣:

{
    a: 1,
    b: 2,
    c: 3
}

           

但是在一些複雜的情況下就有問題了。 例如在

Ajax

中你要傳一個複雜的

json

對像,也就說是對象嵌數組,數組中包括對象,你這樣傳:

application/x-www-form-urlencoded

這種形式是沒有辦法将複雜的

JSON

組織成鍵值對形式。

{
    data: {
        a: [
            {
                x: 2
            }
        ];
    }
}

           

可以用如下方式傳遞複雜的

json

對象

$.ajax({
    dataType: "json",
    contentType: "application/json",
    data: JSON.stringify({ a: [{ b: 1, a: 1 }] })
});

           

七、jQuery 的替代者

近年來前端

MV*

的發展壯大,人們越來越少的使用

jQuery

,我們不可能單獨為了使用

jQuery

Ajax api

來單獨引入他,無可避免的,我們需要尋找新的技術方案。

尤雨溪在他的文檔中推薦大家用

axios

進行網絡請求。

axios

基于

Promise

對原生的

XHR

進行了非常全面的封裝,使用方式也非常的優雅。另外,

axios

同樣提供了在

node

環境下的支援,可謂是網絡請求的首選方案。

未來必定還會出現更優秀的封裝,他們有非常周全的考慮以及詳細的文檔,這裡我們不多做考究,我們把關注的重點放在更底層的 API

fetch

Fetch API

是一個用用于通路和操縱 HTTP 管道的強大的原生 API。

這種功能以前是使用 XMLHttpRequest 實作的。Fetch 提供了一個更好的替代方法,可以很容易地被其他技術使用,例如 Service Workers。Fetch 還提供了單個邏輯位置來定義其他 HTTP 相關概念,例如 CORS 和 HTTP 的擴充。

可見

fetch

是作為

XMLHttpRequest

的替代品出現的。

使用

fetch

,你不需要再額外加載一個外部資源。但它還沒有被浏覽器完全支援,是以你仍然需要一個

polyfill

八、fetch 的使用

一個基本的 fetch 請求:

const options = {
    method: "POST", // 請求參數
    headers: { "Content-Type": "application/json" }, // 設定請求頭
    body: JSON.stringify({ name: "123" }), // 請求參數
    credentials: "same-origin", // cookie設定
    mode: "cors" // 跨域
};
fetch("http://www.xxx.com")
    .then(function(response) {
        return response.json();
    })
    .then(function(myJson) {
        console.log(myJson); // 響應資料
    })
    .catch(function(err) {
        console.log(err); // 異常處理
    });

           

Fetch API

提供了一個全局的

fetch()

方法,以及幾個輔助對象來發起一個網絡請求。

全面分析前端的網絡請求方式
  • fetch()

fetch()

方法用于發起擷取資源的請求。它傳回一個

promise

,這個

promise

會在請求響應後被

resolve

,并傳回

Response

對象。

  • Headers

可以通過

Headers()

構造函數來建立一個你自己的

headers

對象,相當于

response/request

的頭資訊,可以使你查詢到這些頭資訊,或者針對不同的結果做不同的操作。

var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");

           
  • Request

通過

Request()

構造函數可以建立一個

Request

對象,這個對象可以作為

fetch

函數的第二個參數。

  • Response

fetch()

處理完

promises

之後傳回一個

Response

執行個體,也可以手動建立一個

Response

執行個體。

九、fetch polyfill 源碼分析

由于

fetch

是一個非常底層的

API

,是以我們無法進一步的探究它的底層,但是我們可以借助它的

polyfill

探究它的基本原理,并找出其中的坑點。

代碼結構

全面分析前端的網絡請求方式

由代碼可見,

polyfill

主要對

Fetch

API 提供的四大對象進行了封裝:

fetch 封裝

全面分析前端的網絡請求方式

代碼非常清晰:

  • 構造一個

    Promise

    對象并傳回
  • 建立一個

    Request

    對象
  • 建立一個

    XMLHttpRequest

    對象
  • 取出

    Request

    對象中的請求

    url

    ,請求方發,

    open

    一個

    xhr

    請求,并将

    Request

    對象中存儲的

    headers

    取出賦給 xhr
  • xhr onload

    後取出

    response

    status

    headers

    body

    封裝

    Response

    對象,調用

    resolve

異常處理

全面分析前端的網絡請求方式

可以發現,調用

reject

有三種可能:

  • 1.請求逾時
  • 2.請求失敗

注意:當和伺服器建立簡介,并收到伺服器的異常狀态碼如

404、500

等并不能觸發

onerror

。當網絡故障時或請求被阻止時,才會标記為

reject

,如跨域、

url

不存在,網絡異常等會觸發

onerror

是以使用 fetch 當接收到異常狀态碼都是會進入 then 而不是 catch。這些錯誤請求往往要手動處理。

  • 3.手動終止

可以在

request

參數中傳入

signal

對象,并對

signal

對象添加

abort

事件監聽,當

xhr.readyState

變為

4

(響應内容解析完成)後将 signal 對象的 abort 事件監聽移除掉。

這表示,在一個

fetch

請求結束之前可以調用

signal.abort

将其終止。在浏覽器中可以使用

AbortController()

構造函數建立一個控制器,然後使用

AbortController.signal

屬性

這是一個實驗中的功能,此功能某些浏覽器尚在開發中

Headers 封裝

全面分析前端的網絡請求方式

在 header 對象中維護了一個

map

對象,構造函數中可以傳入

Header

對象、數組、普通對象類型的

header

,并将所有的值維護到

map

中。

之前在

fetch

函數中看到調用了

header

forEach

方法,下面是它的實作:

全面分析前端的網絡請求方式

可見

header

的周遊即其内部

map

的周遊。

另外

Header

還提供了

append、delete、get、set

等方法,都是對其内部的

map

對象進行操作。

Request 對象

全面分析前端的網絡請求方式

Request

對象接收的兩個參數即

fetch

函數接收的兩個參數,第一個參數可以直接傳遞

url

,也可以傳遞一個構造好的

request

對象。第二個參數即控制不同配置的

option

對象。

可以傳入

credentials、headers、method、mode、signal、referrer

等屬性。

這裡注意:

  • 傳入的

    headers

    被當作

    Headers

    構造函數的參數來構造 header 對象。

cookie 處理

fetch 函數中還有如下的代碼:

if (request.credentials === "include") {
    xhr.withCredentials = true;
} else if (request.credentials === "omit") {
    xhr.withCredentials = false;
}

           

預設的

credentials

類型為

same-origin

,即可攜帶同源請求的 coodkie。

然後我發現這裡 polyfill 的實作和MDN-使用 Fetch以及很多資料是不一緻的:

mdn: 預設情況下,fetch 不會從服務端發送或接收任何 cookies

于是我分别實驗了下使用

polyfill

和使用原生

fetch

攜帶 cookie 的情況,發現在不設定

credentials

的情況下居然都是預設攜帶同源

cookie

的,這和文檔的說明說不一緻的,查閱了許多資料後都是說

fetch

預設不會攜帶 cookie,下面是使用原生

fetch

在浏覽器進行請求的情況:

全面分析前端的網絡請求方式

然後我發現在已經指出新版浏覽器

credentials

預設值已更改為

same-origin

,舊版依然是

omit

确實MDN-使用 Fetch這裡的文檔更新的有些不及時,誤人子弟了…

Response 對象

Response

對象是

fetch

調用成功後的傳回值:

回顧下

f

etch

中對

Response`的操作:

xhr.onload = function() {
    var options = {
        status: xhr.status,
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || "")
    };
    options.url =
        "responseURL" in xhr
            ? xhr.responseURL
            : options.headers.get("X-Request-URL");
    var body = "response" in xhr ? xhr.response : xhr.responseText;
    resolve(new Response(body, options));
};

           

Response

構造函數:

全面分析前端的網絡請求方式

可見在構造函數中主要對

options

中的

status、statusText、headers、url

等分别做了處理并挂載到

Response

對象上。

構造函數裡面并沒有對

responseText

的明确處理,最後交給了

_initBody

函數處理,而

Response

并沒有主動聲明

_initBody

屬性,代碼最後使用

Response

調用了

Body

函數,實際上

_initBody

函數是通過

Body

函數挂載到

Response

身上的,先來看看

_initBody

函數:

全面分析前端的網絡請求方式

可見,

_initBody

函數根據

xhr.response

的類型(

Blob、FormData、String...

),為不同的參數進行指派,這些參數在

Body

方法中得到不同的應用,下面具體看看

Body

函數還做了哪些其他的操作:

全面分析前端的網絡請求方式

Body

函數中還為

Response

對象挂載了四個函數,

text、json、blob、formData

,這些函數中的操作就是将_initBody 中得到的不同類型的傳回值傳回。

這也說明了,在

fetch

執行完畢後,不能直接在

response

中擷取到傳回值而必須調用

text()、json()

等函數才能擷取到傳回值。

這裡還有一點需要說明:幾個函數中都有類似下面的邏輯:

var rejected = consumed(this);
if (rejected) {
    return rejected;
}

           

consumed 函數:

function consumed(body) {
    if (body.bodyUsed) {
        return Promise.reject(new TypeError("Already read"));
    }
    body.bodyUsed = true;
}

           

每次調用

text()、json()

等函數後會将

bodyUsed

變量變為

true

,用來辨別傳回值已經讀取過了,下一次再讀取直接抛出

TypeError(\'Already read\')

。這也遵循了原生

fetch

的原則:

因為 Responses 對象被設定為了 stream 的方式,是以它們隻能被讀取一次

十、fetch 的坑點

VUE

的文檔中對

fetch

有下面的描述:

使用

fetch

還有很多别的注意事項,這也是為什麼大家現階段還是更喜歡

axios

多一些。當然這個事情在未來可能會發生改變。

由于

fetch

是一個非常底層的

API

,它并沒有被進行很多封裝,還有許多問題需要處理:

  • 不能直接傳遞

    JavaScript

    對象作為參數
  • 需要自己判斷傳回值類型,并執行響應擷取傳回值的方法
  • 擷取傳回值方法隻能調用一次,不能多次調用
  • 無法正常的捕獲異常
  • 老版浏覽器不會預設攜帶

    cookie

  • 不支援

    jsonp

十一、對 fetch 的封裝

請求參數處理

支援傳入不同的參數類型:

function stringify(url, data) {
    var dataString = url.indexOf("?") == -1 ? "?" : "&";
    for (var key in data) {
        dataString += key + "=" + data[key] + "&";
    }
    return dataString;
}

if (request.formData) {
    request.body = request.data;
} else if (/^get$/i.test(request.method)) {
    request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) {
    request.headers.set(
        "Content-Type",
        "application/x-www-form-urlencoded;charset=UTF-8"
    );
    request.body = stringify(request.data);
} else {
    request.headers.set("Content-Type", "application/json;charset=UTF-8");
    request.body = JSON.stringify(request.data);
}

           

cookie 攜帶

fetch

在新版浏覽器已經開始預設攜帶同源

cookie

,但在老版浏覽器中不會預設攜帶,我們需要對他進行統一設定:

request.credentials = "same-origin"; // 同源攜帶
request.credentials = "include"; // 可跨域攜帶

           

異常處理

當接收到一個代表錯誤的 HTTP 狀态碼時,從 fetch()傳回的 Promise 不會被标記為 reject, 即使該 HTTP 響應的狀态碼是 404 或 500。相反,它會将 Promise 狀态标記為 resolve (但是會将 resolve 的傳回值的 ok 屬性設定為 false ),僅當網絡故障時或請求被阻止時,才會标記為 reject。

是以我們要對

fetch

的異常進行統一處理

.then(response => {
  if (response.ok) {
    return Promise.resolve(response);
  }else{
    const error = new Error(`請求失敗! 狀态碼: ${response.status}, 失敗資訊: ${response.statusText}`);
    error.response = response;
    return Promise.reject(error);
  }
});

           

傳回值處理

對不同的傳回值類型調用不同的函數接收,這裡必須提前判斷好類型,不能多次調用擷取傳回值的方法:

.then(response => {
  let contentType = response.headers.get(\'content-type\');
  if (contentType.includes(\'application/json\')) {
    return response.json();
  } else {
    return response.text();
  }
});

           

jsonp

fetch

本身沒有提供對

jsonp

的支援,

jsonp

本身也不屬于一種非常好的解決跨域的方式,推薦使用

cors

或者

nginx

解決跨域,具體請看下面的章節。

fetch 封裝好了,可以愉快的使用了。

嗯,axios 真好用…

十二、跨域總結

談到網絡請求,就不得不提跨域。

浏覽器的同源政策限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行互動。這是一個用于隔離潛在惡意檔案的重要安全機制。通常不允許不同源間的讀操作。

跨域條件:協定,域名,端口,有一個不同就算跨域。

下面是解決跨域的幾種方式:

nginx

使用

nginx

反向代理實作跨域,參考我這篇文章:前端開發者必備的 nginx 知識

cors

CORS

是一個

W3C

标準,全稱是”跨域資源共享”

(Cross-origin resource sharing)

。它允許浏覽器向跨源伺服器,發出

XMLHttpRequest

請求。

服務端設定

Access-Control-Allow-Origin

就可以開啟

CORS

。 該屬性表示哪些域名可以通路資源,如果設定通配符則表示所有網站都可以通路資源。

app.all("*", function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    next();
});

           

jsonp

script

标簽的

src

屬性中的連結可以通路跨域的

js

腳本,利用這個特性,服務端不再傳回

JSON

格式的資料,而是傳回一段調用某個函數的

js

代碼,在

src

中進行了調用,這樣實作了跨域。

jquery

jsonp

的支援:

$.ajax({
    type : "get",
    url : "http://xxxx"
    dataType: "jsonp",
    jsonp:"callback",
    jsonpCallback: "doo",
    success : function(data) {
        console.log(data);
    }
});

           

fetch、axios

等并沒有直接提供對

jsonp

的支援,如果需要使用這種方式,我們可以嘗試進行手動封裝:

(function(window, document) {
    "use strict";
    var jsonp = function(url, data, callback) {
        // 1.将傳入的data資料轉化為url字元串形式
        // {id:1,name:\'jack\'} => id=1&name=jack
        var dataString = url.indexof("?") == -1 ? "?" : "&";
        for (var key in data) {
            dataString += key + "=" + data[key] + "&";
        }

        // 2 處理url中的回調函數
        // cbFuncName回調函數的名字 :my_json_cb_名字的字首 + 随機數(把小數點去掉)
        var cbFuncName =
            "my_json_cb_" +
            Math.random()
                .toString()
                .replace(".", "");
        dataString += "callback=" + cbFuncName;

        // 3.建立一個script标簽并插入到頁面中
        var scriptEle = document.createElement("script");
        scriptEle.src = url + dataString;

        // 4.挂載回調函數
        window[cbFuncName] = function(data) {
            callback(data);
            // 處理完回調函數的資料之後,删除jsonp的script标簽
            document.body.removeChild(scriptEle);
        };

        document.body.appendChild(scriptEle);
    };

    window.$jsonp = jsonp;
})(window, document);

           

postMessage 跨域

postMessage()

方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實作跨文本檔、多視窗、跨域消息傳遞。

//捕獲iframe
var domain = "http://scriptandstyle.com";
var iframe = document.getElementById("myIFrame").contentWindow;

//發送消息
setInterval(function() {
    var message = "Hello!  The time is: " + new Date().getTime();
    console.log("blog.local:  sending message:  " + message);
    //send the message and target URI
    iframe.postMessage(message, domain);
}, 6000);
//響應事件
window.addEventListener(
    "message",
    function(event) {
        if (event.origin !== "http://davidwalsh.name") return;
        console.log("message received:  " + event.data, event);
        event.source.postMessage("holla back youngin!", event.origin);
    },
    false
);

           

postMessage

跨域适用于以下場景:同浏覽器多視窗間跨域通信、

iframe

間跨域通信。

WebSocket

WebSocket

是一種雙向通信協定,在建立連接配接之後,

WebSocket

server

client

都能主動向對方發送或接收資料而不受同源政策的限制。

function WebSocketTest() {
    if ("WebSocket" in window) {
        alert("您的浏覽器支援 WebSocket!");
        // 打開一個 web socket
        var ws = new WebSocket("ws://localhost:3000/abcd");
        ws.onopen = function() {
            // Web Socket 已連接配接上,使用 send() 方法發送資料
            ws.send("發送資料");
            alert("資料發送中...");
        };
        ws.onmessage = function(evt) {
            var received_msg = evt.data;
            alert("資料已接收...");
        };
        ws.onclose = function() {
            // 關閉 websocket
            alert("連接配接已關閉...");
        };
    } else {
        // 浏覽器不支援 WebSocket
        alert("您的浏覽器不支援 WebSocket!");
    }
}

           

文中如有錯誤,歡迎在評論區指正,謝謝閱讀。