前端面試題系列之-ajax及其他與服務(wù)器交互篇

手寫ajax

GET請求


let xmlhttp侮攀;
if (window.XMLHttpRequest)
{
    // IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執(zhí)行代碼
    xmlhttp=new XMLHttpRequest();
}
else
{
    // IE6, IE5 瀏覽器執(zhí)行代碼
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
        console.log(xmlhttp.response);  // 設(shè)置responseType為json時,用response獲取數(shù)據(jù),無需JSON.parse
    }
    if (xmlhttp.readyState === 4 && xmlhttp.status !== 200) {
        throw new Error(`請求出錯,錯誤碼:${xmlhttp.status}摩泪,錯誤說明: ${xmlhttp.statusText}`);
    }
}
xmlhttp.open("GET", url, true);
// 服務(wù)器返回的數(shù)據(jù)為json數(shù)據(jù),不需要parse轉(zhuǎn)換
// 不設(shè)置的話客戶端默認(rèn)收到的是字符串劫谅,客戶端需要parse轉(zhuǎn)換成json數(shù)據(jù)
xmlhttp.responseType = "json";
xmlhttp.send();

POST請求

let xmlhttp见坑;
if (window.XMLHttpRequest)
{
    // IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執(zhí)行代碼
    xmlhttp=new XMLHttpRequest();
}
else
{
    // IE6, IE5 瀏覽器執(zhí)行代碼
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
        console.log(xmlhttp.response);
    }
    if (xmlhttp.readyState === 4 && xmlhttp.status !== 200) {
        throw new Error(`請求出錯,錯誤碼:${xmlhttp.status}捏检,錯誤說明: ${xmlhttp.statusText}`);
    }
}
xmlhttp.open("POST", url, true);
// 發(fā)送給服務(wù)器的為json數(shù)據(jù)荞驴,即請求頭的content-type類型為json類型
// 不設(shè)置的話服務(wù)器默認(rèn)收到的是字符串,服務(wù)器需要parse轉(zhuǎn)換成json數(shù)據(jù)類型
xmlhttp.setRequestHeader("Content-Type", "application/json");
// 服務(wù)器返回的數(shù)據(jù)為json數(shù)據(jù)贯城,不需要parse轉(zhuǎn)換
// 不設(shè)置的話客戶端默認(rèn)收到的是字符串熊楼,客戶端需要parse轉(zhuǎn)換成json數(shù)據(jù)
xmlhttp.responseType = "json";
xmlhttp.send(JSON.stringify({method: "ajax",sendType: "post"}));

發(fā)送json數(shù)據(jù)

方法一

設(shè)置xmlhttp.responseType,發(fā)送時用JSON.stringify將對象轉(zhuǎn)化為字符串能犯,添加請求頭content-type類型為application/json鲫骗,接收數(shù)據(jù)用xmlhttp.response

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
        console.log(xmlhttp.response);
    }
}
xmlhttp.open("POST", url, true);
// 發(fā)送給服務(wù)器的為json數(shù)據(jù),即請求頭的content-type類型為json類型
// 不設(shè)置的話服務(wù)器默認(rèn)收到的是字符串踩晶,服務(wù)器需要parse轉(zhuǎn)換成json數(shù)據(jù)類型
xmlhttp.setRequestHeader("Content-Type", "application/json");
// 服務(wù)器返回的數(shù)據(jù)為json數(shù)據(jù)执泰,不需要parse轉(zhuǎn)換
// 不設(shè)置的話客戶端默認(rèn)收到的是字符串,客戶端需要parse轉(zhuǎn)換成json數(shù)據(jù)
xmlhttp.responseType = "json";
xmlhttp.send(JSON.stringify({method: "ajax",sendType: "post"}));
方法二

發(fā)送時JSON.stringify格式化發(fā)送數(shù)據(jù)為json渡蜻,服務(wù)器接收數(shù)據(jù)后需要調(diào)用JSON.parse將字符串轉(zhuǎn)化為對象术吝,服務(wù)器發(fā)送數(shù)據(jù)給客戶端時,需要調(diào)用JSON.stringify茸苇∨挪裕客戶端接收數(shù)據(jù)時需要調(diào)用JSON.parse

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
        console.log(JSON.parse(xmlhttp.responseText));
    }
}
xmlhttp.open("POST", url, true);
xmlhttp.send(JSON.stringify({method: "ajax",sendType: "post"}));

設(shè)置超時

// 默認(rèn)為0,即為不超時
xmlhttp.timeout = 4000;

跨域攜帶cookie

xhr.withCredentials = true;

終止請求

xmlhttp.abort(); 

設(shè)置請求頭

xmlhttp.setRequestHeader("Content-Type", "application/json");

設(shè)置響應(yīng)類型

// ""(默認(rèn)值等同于text)学密、arraybuffer淘衙、blob、document则果、json幔翰、text
// 其中如果設(shè)置json及document,但是服務(wù)器返回的據(jù)不是指定類型的化西壮,response的值為null
xmlhttp.responseType = "json";

強制設(shè)置響應(yīng)類型

xmlhttp.overrideMimeType('text/xml');

獲取響應(yīng)頭

xmlhttp.getResponseHeader("Content-Type")

axios的用法

GET請求

axios.get(url).then(function (response) {
    console.log(response);
}).catch(function (error) {
    console.log(error);
});

POST請求

axios.post(url, {
    "firstName": 'Fred',
    "lastName": 'Flintstone'
}).then(function (response) {
    console.log(response);
}).catch(function (error) {
    console.log(error);
});

添加請求頭

  // `headers` are custom headers to be sent
  headers: {'X-Requested-With': 'XMLHttpRequest'},

設(shè)置超時時間

  // 請求超時時間(毫秒)
  timeout: 1000,

跨域攜帶cookie

  // 是否攜帶cookie信息
  withCredentials: false, // default

設(shè)置響應(yīng)類型

  // 響應(yīng)格式
  // 可選項 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType: 'json', // 默認(rèn)值是json

終止請求

var CancelToken = axios.CancelToken;var source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }});

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
var CancelToken = axios.CancelToken;var cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // An executor function receives a cancel function as a parameter
    cancel = c;
  })});

// cancel the request
cancel();

相關(guān)鏈接:
axios中文文檔

fetch的用法

Fetch底層并不是XMLHttp技術(shù)(ajax)遗增,F(xiàn)etch 的核心在于對 HTTP 接口的抽象,包括 Request款青,Response做修,Headers,Body,以及用于初始化異步請求的 global fetch饰及。得益于 JavaScript 實現(xiàn)的這些抽象好的 HTTP 模塊蔗坯,其他接口能夠很方便的使用這些功能。除此之外燎含,F(xiàn)etch 還利用到了請求的異步特性——它是基于 Promise 的宾濒。

GET請求

fetch(url)
    .then(function (response) {
        // debugger;
        // let data = response.text(); // json 數(shù)據(jù)轉(zhuǎn)字符串,不報錯
        // let data = response.formData(); // json 報錯 類型錯誤
        // let data = response.json(); // json 不報錯屏箍,json
        // let data = response.arrayBuffer(); // json 不報錯绘梦, arraybuffer
        // let data = response.blob(); // json 不報錯, blob
        // let data = response.text(); // string  不報錯 string
        // let data = response.formData(); // string 報錯 類型錯誤
        // let data = response.json(); // string 報錯 類型錯誤
        // let data = response.arrayBuffer(); // string 不報錯赴魁, arraybuffer
        // let data = response.blob(); // string 不報錯卸奉, blob
        return data;
    })
    .then(function (myJson) {
        // debugger;
        console.log(myJson);
    });

注意:

  • 第一個then方法為已經(jīng)發(fā)送成功并收到服務(wù)器返回的數(shù)據(jù),準(zhǔn)備處理response響應(yīng)體數(shù)據(jù)颖御,第二個then方法為處理完成response響應(yīng)體數(shù)據(jù)榄棵。
  • 響應(yīng)體數(shù)據(jù)處理時,如果服務(wù)器返回數(shù)據(jù)與準(zhǔn)備獲取的類型(let data = response.text() 這里的text即為客戶端準(zhǔn)備獲取的類型)不一致時潘拱,json和formData這兩個會在不一致時報錯疹鳄,其余的不會報錯。與ajax保持類似芦岂。

POST 請求

let data = {
    method: "POST",
    sendData: "adsf"
}
fetch(url, {
    body: JSON.stringify(data),
    headers: {
        'Content-Type': 'application/json' // 不設(shè)置服務(wù)器不識別為json
    },
    method: "POST"
}).then(response => {
        // let data = response.text(); // json 數(shù)據(jù)轉(zhuǎn)字符串尚辑,不報錯
        // let data = response.formData(); // json 報錯 類型錯誤
        let data = response.json(); // json 不報錯,json
        // let data = response.arrayBuffer(); // json 不報錯盔腔, arraybuffer
        // let data = response.blob(); // json 不報錯, blob
        // let data = response.text(); // string  不報錯 string
        // let data = response.formData(); // string 報錯 類型錯誤
        // let data = response.json(); // string 報錯 類型錯誤
        // let data = response.arrayBuffer(); // string 不報錯月褥, arraybuffer
        // let data = response.blob(); // string 不報錯弛随, blob
    return data;
}).then(response => {
    console.log(response);
})

注意:

  • 如果不設(shè)置正確的header中的content-type類型的化,服務(wù)獲取到的數(shù)據(jù)類型并不會是指定類型宁赤,這一點與ajax保持一致

添加請求頭

fetch(url, {
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    }
})
  .then(response => response.json()) // parses response to JSON

獲取響應(yīng)頭

fetch(url, {
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    }
})
.then(response => {
    console.log(response.headers);// 可通過這里的content-type類型決定返回哪種數(shù)據(jù)類型
    
    if(response.headers.get("content-type") === "application/json") {
        return response.json()
    }
    return response.text()
})

跨域攜帶cookie

// 注意fetch在不跨域的情況下是攜帶cookie的舀透,接收set-cookie頭。 在跨域的情況下是不攜帶cookie决左,不接受set-cookie
// 這一點和ajax一致
fetch(url, {credentials: 'include' })

超時設(shè)置

方案一 設(shè)置setTimeout
function request(url, wait) {
    return new Promise((resolve, reject) => {
        let status = 0; // 0 等待 1 完成 2 超時
        // 設(shè)置定時器愕够,超時reject,這個只有一個promise
        let timer = setTimeout(() => {
            if (status === 0) {
                status = 2;
                timer = null;
                console.log("超時");
                reject(new Error("request timeout error"));
            }
        }, wait);
        fetch(url)
            .then(res => res.json())
            .then(res => {
                // 清除定時器
                if (status !== 2) {
                    clearTimeout(timer);
                    timer = null;
                    status = 1;
                    resolve(res);
                }
            });
    });
}
// 調(diào)用

request(url).then(json => {
    console.log(json);
}).catch(err => {
    console.log(err);
    // 處理超時
})
方案二 使用Promise.race
// 創(chuàng)建一個setTimeout的promise和一個fetch的promise佛猛,兩個promise進行賽跑(race)惑芭,如果setTimeout先完成,則結(jié)束promise.race继找。
function request(fetchPromise, timeout) {
    return Promise.race([
        fetchPromise,
        new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("超時了");
                reject(new Error('request timeout'))  // 創(chuàng)建Error
            }, 3000)
        })
    ]);
}
// 調(diào)用
request(fetch(url), 3000).then(response => response.json()).then(json => {
    console.log(json);
}).catch(err => {
    console.log(err)  // 這個就是剛才創(chuàng)建的超時異常遂跟,或其他請求異常
    // 這里處理超時
})

取消請求

方案一 通過reject取消Promise,并不會取消請求的發(fā)送

原理為劫持Promise,添加一個abort方法

// 劫持全局Promise
Promise.prototype.abort = () => Promise.reject(new Error("abort promise"));
// 用函數(shù)包裝
function fetchAbort1(fetchPromise) {
    fetchPromise.abort = () => Promise.reject(new Error("abort promise"));
    return fetchPromise
}
// 示例
var p = fetch(url).then(response => response.json()).then(json => {
    console.log(json);
}).catch(error => {
    console.log(error);
});
setTimeout(() => {
    console.log("4s后取消了")
    p.abort()
}, 4000)

另一種實現(xiàn)(加上Promise.race)

function fetchAbort(fetchPromise) {
    var abort_fn = null;
    //這是一個可以被reject的promise
    var abort_promise = new Promise(function (resolve, reject) {
        abort_fn = function () {
            reject('abort promise');
        };
    });
    //這里使用Promise.race幻锁,以最快 resolve 或 reject 的結(jié)果來傳入后續(xù)綁定的回調(diào)
    var abortable_promise = Promise.race([
        fetchPromise,
        abort_promise
    ]);
    abortable_promise.abort = abort_fn;
    return abortable_promise;
}
// 調(diào)用
var p = fetchAbort(fetch('//a.com/b/c'));
p.then(function(res) {
    console.log(res)
}, function(err) {
    console.log(err);
});
//假設(shè)fetch要3秒凯亮,但是你想在2秒就放棄了:
setTimeout(function() {
    p.abort(); // -> will print "abort promise"
}, 2000);
方案二 通過AbortController 取消fetch請求(處于實驗階段,慎用)哄尔,會取消請求的發(fā)送假消,終止fetch請求
let controller = new AbortController();
let signal = controller.signal;

let timeoutPromise = (timeout) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(new Response("timeout", { status: 504, statusText: "timeout " }));
            controller.abort();
        }, timeout);
    });
}
let requestPromise = (url) => {
    return fetch(url, {
        signal: signal
    });
};
Promise.race([timeoutPromise(1000), requestPromise("https://www.baidu.com")])
.then(resp => {
    console.log(resp);
})
.catch(error => {
    console.log(error);
});

參考鏈接:
Fetch超時設(shè)置和終止請求
AbortController

postMessage

postMessage()方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實現(xiàn)跨文本檔岭接、多窗口富拗、跨域消息傳遞,只適用于有iframe標(biāo)簽的頁面

父頁面

<iframe src="./child1.html" frameborder="1"></iframe>
<iframe src="./child2.html" frameborder="1"></iframe>
<script>
    window.onload = function(params) {
        var child1 = window.frames[0];
        child1.postMessage('message from parentwindow', '*');// 給1發(fā)消息
        var child2 = window.frames[1];
        child2.postMessage('message from parentwindow', '*');// 給1發(fā)消息
        window.addEventListener('message', function (e) {
            console.log(e.data)  // 接收消息
        }, false)
    }
</script>

子頁面一

<h1>child1 page</h1>
<script>
    window.onload = function() {
        window.top.postMessage('message from child1', "*");  // 給父頁面發(fā)消息
        window.addEventListener('message', function (e) {
            console.log("child1", e.data);  // 接收數(shù)據(jù)
        }, false)
    }
</script>

子頁面二

<h1>child2 page</h1>
<script>
    window.onload = function() {
        window.top.postMessage('message from child2', "*");  // 給父頁面發(fā)消息
        window.addEventListener('message', function (e) {
            console.log("child2", e.data);  // 接收數(shù)據(jù)
        }, false)
    }
</script>

參考連接:
window.postMessage

websocket

// 需要自定義頭協(xié)議
// 需要保持心跳連接
// 需要斷開重連

const sleep = time => new Promise(resolve => {
    setTimeout(resolve, time);
})

let count = 0; // 鏈接次數(shù)
function soket(url, wsType = "arraybuffer") {
    var ws = new WebSocket(url);
    ws.binaryType = wsType;  // 設(shè)置數(shù)據(jù)接收類型
    ws.onopen = () => {
        ws.send("Hello world!");  // 建立連接后發(fā)送驗證消息
    };
    ws.onmessage = evt => {
        console.log(evt.data);  // 接收消息后的處理
        ws.send("abcd");
    }
    ws.onclose = async() => {
        console.log("ws 連接失敗")  // 斷開連接后的處理
        count++;
        if (count < 5) {
            await sleep(500);
            soket(url, wsType);
        }
    };
    return ws;
}

Server Sent Events

嚴(yán)格地說亿傅,HTTP 協(xié)議無法做到服務(wù)器主動推送信息媒峡。但是,有一種變通方法葵擎,就是服務(wù)器向客戶端聲明谅阿,接下來要發(fā)送的是流信息(streaming)。

也就是說酬滤,發(fā)送的不是一次性的數(shù)據(jù)包签餐,而是一個數(shù)據(jù)流,會連續(xù)不斷地發(fā)送過來盯串。這時氯檐,客戶端不會關(guān)閉連接,會一直等著服務(wù)器發(fā)過來的新的數(shù)據(jù)流体捏,視頻播放就是這樣的例子冠摄。本質(zhì)上,這種通信就是以流信息的方式几缭,完成一次用時很長的下載河泳。

SSE 就是利用這種機制,使用流信息向瀏覽器推送信息年栓。它基于 HTTP 協(xié)議拆挥,目前除了 IE/Edge,其他瀏覽器都支持某抓。

SSE 與 WebSocket 作用相似纸兔,都是建立瀏覽器與服務(wù)器之間的通信渠道,然后服務(wù)器向瀏覽器推送信息否副。

總體來說汉矿,WebSocket 更強大和靈活。因為它是全雙工通道副编,可以雙向通信负甸;SSE 是單向通道流强,只能服務(wù)器向瀏覽器發(fā)送,因為流信息本質(zhì)上就是下載呻待。如果瀏覽器向服務(wù)器發(fā)送信息打月,就變成了另一次 HTTP 請求。

SSE和websoket的區(qū)別

  • SSE 使用 HTTP 協(xié)議蚕捉,現(xiàn)有的服務(wù)器軟件都支持奏篙。WebSocket 是一個獨立協(xié)議。
  • SSE 屬于輕量級迫淹,使用簡單秘通;WebSocket 協(xié)議相對復(fù)雜。
  • SSE 默認(rèn)支持?jǐn)嗑€重連敛熬,WebSocket 需要自己實現(xiàn)肺稀。
  • SSE 一般只用來傳送文本,二進制數(shù)據(jù)需要編碼后傳送应民,WebSocket 默認(rèn)支持傳送二進制數(shù)據(jù)话原。
  • SSE 支持自定義發(fā)送的消息類型。

基本用法

// 客戶端
// 判斷是否支持
if("EventSource" in window) {
    // 生成EventSource實例
    var source = new EventSource(url);
    // 建立連接時觸發(fā)
    source.onopen = function (event) {
      // ...
    };
    // 接收消息時觸發(fā)
    source.onmessage = function (event) {
      var data = event.data;
      // handle message
    };
    // 發(fā)生通信錯誤時觸發(fā)
    source.onerror = function (event) {
      // handle error event
    };
}
// 服務(wù)器端
// 發(fā)送以下響應(yīng)頭
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

// 發(fā)送響應(yīng)體數(shù)據(jù)示例诲锹,每一條數(shù)據(jù)以\n\n結(jié)尾繁仁,每行按\n分割

: this is a test stream\n\n

data: some text\n\n

data: another message\n
data: with two lines \n\n

服務(wù)器發(fā)送的響應(yīng)體字段

data
// 數(shù)據(jù)內(nèi)容用data字段表示。

data: begin message\n  // \n為換行
data: continue message\n\n  // \n\n為消息結(jié)尾

data: {\n
data: "foo": "bar",\n
data: "baz", 555\n
data: }\n\n
event
// event字段表示自定義的事件類型归园,默認(rèn)是message事件黄虱。瀏覽器可以用addEventListener()監(jiān)聽該事件。

event: foo\n
data: a foo event\n\n

data: an unnamed event\n\n

event: bar\n
data: a bar event\n\n
id
// id為每一條數(shù)據(jù)的標(biāo)識庸诱,可在連接斷線后捻浦,重新連接時同步信息使用, 因此,這個頭信息可以被視為一種同步機制桥爽∧矗客戶端可以用lastEventId屬性讀取這個值
id: msg1\n
retry
// 服務(wù)器端發(fā)送以下數(shù)據(jù),客戶端會在報錯后等待指定時間重新連接
// 適用于服務(wù)器主動關(guān)閉連接聚谁、網(wǎng)絡(luò)出錯及超時、時間間隔到期等滞诺。
retry: 10000\n

關(guān)閉SSE連接

source.close();

跨域攜帶cookie

var source = new EventSource(url, { withCredentials: true });

自定義事件

// 客戶端 自定義事件foo
source.addEventListener('foo', function (event) {
  var data = event.data;
  // handle message
}, false);
// 服務(wù)器端 發(fā)送自定義事件foo
event: foo\n
data: a foo event\n\n

關(guān)閉形导、超時及網(wǎng)絡(luò)出錯時的重連

retry: 10000\n

示例

客戶端

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>測試SSE</title>
</head>
<body>
    <div id="example"></div>
    <script>
        var source = new EventSource('http://127.0.0.1:8844/stream');
        var div = document.getElementById('example');
        source.onopen = function (event) {
            div.innerHTML += '<p>Connection open ...</p>';
        };
        source.onerror = function (event) {
            div.innerHTML += '<p>Connection close.</p>';
        };
        source.addEventListener('connecttime', function (event) {
            div.innerHTML += ('<p>Start time: ' + event.data + '</p>');
        }, false);
        source.onmessage = function (event) {
            div.innerHTML += ('<p>Ping: ' + event.data + '</p>');
        };
    </script>
</body>
</html>

服務(wù)器端

var http = require("http");
var count = 0;
http.createServer(function (req, res) {
    var fileName = "." + req.url;
    count++;
    if (fileName === "./stream") {
        res.writeHead(200, {
            "Content-Type": "text/event-stream",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Access-Control-Allow-Origin": '*',
        });
        res.write(`: This is a ${count} comment\n`)
        res.write("retry: 3000\n");
        res.write(`id: ${count}\n`)
        res.write("event: connecttime\n");
        res.write("data: " + (new Date()) + "\n\n");
        // res.end();  // 服務(wù)器關(guān)閉,客戶端在retry時間段后會重新向服務(wù)器發(fā)送連接
        
        // 定時發(fā)送數(shù)據(jù)給客戶端
        interval = setInterval(function () {
            res.write("data: " + (new Date()) + "\n\n");
        }, 1000);
        // 關(guān)閉時清除定時器
        req.connection.addListener("close", function () {
            clearInterval(interval);
        }, false);
    }
}).listen(8844, "127.0.0.1");

相關(guān)連接:
Server-Sent Events 教程
Server-sent events

ajax习霹、axios朵耕、fetch的區(qū)別

  • ajax:底層是ajax,可設(shè)置超時淋叶,可取消請求阎曹,沒有promise封裝,自己處理非200的錯誤
  • axios:底層是ajax,可設(shè)置超時处嫌,可取消請求栅贴,通過promise封裝,非200的錯誤進入catch
  • fetch:底層是fetch API熏迹,不可設(shè)置超時檐薯,不可取消請求,通過promise封裝注暗,非200的錯誤并不會catch

ajax斷點續(xù)傳

什么是短鏈接坛缕、長連接、短輪詢捆昏、長輪詢

  • 短鏈接:請求-響應(yīng)赚楚,響應(yīng)完成后即關(guān)閉連接的為短鏈接
  • 長連接:建立完成連接后,不會主動關(guān)閉連接骗卜,長時間保持客戶端與服務(wù)器端的通訊
  • 短輪詢:客戶端向服務(wù)器發(fā)送請求宠页,連接建立很短時間后,服務(wù)器關(guān)閉請求膨俐,隨即客戶端再次向服務(wù)器發(fā)送請求勇皇。 與長輪詢相比連接持續(xù)時間短,建立次數(shù)多焚刺。
  • 長輪詢:客戶端向服務(wù)器發(fā)送請求敛摘,連接建立很長時間后,服務(wù)器關(guān)閉請求乳愉,隨即客戶端再次向服務(wù)器發(fā)送請求兄淫。與短輪詢相比連接持續(xù)時間長,建立次數(shù)少蔓姚。

什么是跨域捕虽?什么是同源策略,什么是cors

什么是跨域

跨域坡脐,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本泄私。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實施的安全限制备闲。"協(xié)議+域名+端口"不同會產(chǎn)生跨域晌端。

什么是同源策略

同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器恬砂,它是瀏覽器最核心也最基本的安全功能咧纠,如果缺少了同源策略,瀏覽器很容易受到XSS泻骤、CSFR等攻擊漆羔。所謂同源是指"協(xié)議+域名+端口"三者相同梧奢,即便兩個不同的域名指向同一個ip地址,也非同源演痒。

同源策略限制以下幾種行為:

  • Cookie亲轨、LocalStorage 和 IndexDB 無法讀取
  • DOM 和 Js對象無法獲得
  • AJAX 請求不能發(fā)送

什么是cors

CORS是一個W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)嫡霞。
它允許瀏覽器向跨源服務(wù)器瓶埋,發(fā)出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制诊沪。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true

跨域的幾種方式

  1. 通過jsonp跨域
  2. document.domain + iframe跨域
  3. location.hash + iframe
  4. window.name + iframe跨域
  5. postMessage跨域
  6. 跨域資源共享(CORS)
  7. nginx代理跨域
  8. nodejs中間件代理跨域
  9. WebSocket協(xié)議跨域

參考鏈接
詳解跨域(最全的解決方案)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末养筒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子端姚,更是在濱河造成了極大的恐慌晕粪,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渐裸,死亡現(xiàn)場離奇詭異巫湘,居然都是意外死亡,警方通過查閱死者的電腦和手機昏鹃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門尚氛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洞渤,你說我怎么就攤上這事阅嘶。” “怎么了载迄?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵讯柔,是天一觀的道長。 經(jīng)常有香客問我护昧,道長魂迄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任惋耙,我火速辦了婚禮捣炬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绽榛。我一直安慰自己遥金,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布蒜田。 她就那樣靜靜地躺著,像睡著了一般选泻。 火紅的嫁衣襯著肌膚如雪冲粤。 梳的紋絲不亂的頭發(fā)上美莫,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音梯捕,去河邊找鬼厢呵。 笑死,一個胖子當(dāng)著我的面吹牛傀顾,可吹牛的內(nèi)容都是我干的襟铭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼短曾,長吁一口氣:“原來是場噩夢啊……” “哼寒砖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嫉拐,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哩都,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婉徘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漠嵌,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年盖呼,在試婚紗的時候發(fā)現(xiàn)自己被綠了儒鹿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡几晤,死狀恐怖约炎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锌仅,我是刑警寧澤章钾,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站热芹,受9級特大地震影響贱傀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伊脓,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一府寒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧报腔,春花似錦株搔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至翻诉,卻和暖如春炮姨,著一層夾襖步出監(jiān)牢的瞬間捌刮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工舒岸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绅作,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓蛾派,卻偏偏與公主長得像俄认,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子洪乍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359