本文就是對ajax
方面的知識作一個總結,沒有什麼深刻的地方。雖然總結的文章有不少,可是看本身寫的和看別人的文章感受終究仍是相去甚遠的。因此若是讀者以爲內容重複請直接右上角。javascript
用法:html
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >= 200 & xhr.status < 300 || xhr.status === 304) {
}
}
};
xhr.open('GET', 'http://localhost:8080', true);
// 若是是POST請求,則send的參數是具體的數據
xhr.send(null);
複製代碼
readystatechange
:每當xhr.readyState
改變時觸發。timeout
:當請求發出超過xhr.timeout
設置的時間後,依然沒有收到響應,則會觸發。若是在超時終止請求以後再調用訪問status
等屬性就會致使錯誤,因此最好在onreadystatechange
事件中使用try-catch
。loadstart
:收到響應1byte
後觸發。progress
:其event.tartget === xhr
,event.lengthComputable
表示進度信息是否可用,event.total:Content-Lengt
的預期字節數。event.loaded:
已接收的字節數。(須要服務器返回Content-Length
頭部,不然lengthComputable
一直爲false
)。error
:請求出錯觸發。abort
:xhr.abort();
終止鏈接時觸發。load
:接受到完整數據時觸發,至關於readyState === 4時loadend
:通訊完成,不論是error、abort
或者load
,都會致使此事件的觸發(沒有瀏覽器實現)。ps: 爲確保兼容性、正常執行,
onreadystatechange、progress
最好在open
以前綁定。前端
responseText
:做爲響應主體被返回的文本responseXML
:若是返回類型是"text/xml" || "application/xml"
則這個屬性保存這XML DOM
文檔。不然爲null
。status
:http
狀態碼statusText
:狀態碼的說明readyState
:取值以下:
open()
;open()
;send()
;readyState
變化的時候都會觸發onreadystatechange
事件,並且這個事件最好在open
以前就綁定(爲了兼容性)。timeout
:超時時間(ms)。abort
:用於取消異步請求。setRequestHeader(key, value)
:open()
後send()
前調用。getResponseHeader/getAllResponseHeaders
:看名字,不解釋了。overrideMinmeType
:重寫xhr
響應的MIME
(最好在send
以前調用,這樣能夠確保絕對有效)。並非全部的事件都是異步的, xhr.onreadystatechange 和xhr.onloadstart就是同步事件。java
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => console.log('ready state change');
xhr.onloadstart = () => console.log('load start');
xhr.open(method, url);
xhr.send();
console.log('sync');
// 因此結果爲 ready state change => load start => sync
複製代碼
用法:web
/** * 此處的request、response見下文 */
fetch(request)
.then(response => {
response.json()
.then(data => console.log(data));
})
.catch(err => console.log(err));
複製代碼
能夠經過new Request();
建立request
對象(固然也能夠直接寫)ajax
let request = new Request('http://localhost:8080', {
// headers見下文
headers,
method: 'GET',
mode: 'cors'
});
複製代碼
request
上的方法:json
method
: 支持GET
,POST
,PUT
,DELETE
,HEAD
url
:請求的 URL
headers
: 對應的Headers
對象referrer
: 請求的 referrer
信息mode
: 能夠設置cors
,no-cors
,same-origin
credentials
: 設置 cookies
是否隨請求一塊兒發送。能夠設置:omit
,same-origin
redirect
:follow
,error
,manual
integrity
: subresource
完整性值(integrity value
)cache
: 設置 cache
模式 (default
,reload
,no-cache
)能夠經過new Header();
來建立請求頭:跨域
let headers = new Headers({'Content-Type': 'text/plain'});
headers.append('accept', 'text/*');
複製代碼
定義在Headers之上的一些方法以下:promise
fetch().then(response);
中的response
就是一個Response
對象 能夠經過new Request();
建立request
對象瀏覽器
clone()
: 建立一個新的 Response
克隆對象.error()
: 返回一個新的,與網絡錯誤相關的 Response
對象.redirect()
: 重定向,使用新的 URL
建立新的 response
對象..arrayBuffer()
: Returns a promise that resolves with an ArrayBuffer.blob()
: 返回一個 promise
, resolves
是一個 Blob
.formData()
: 返回一個 promise
, resolves
是一個 FormData
對象.json()
: 返回一個 promise
, resolves
是一個 JSON
對象.text()
: 返回一個 promise
, resolves
是一個 USVString (text)
.同源就是擁有相同的協議(protocol) && 主機(hostname) && 端口(port)
,那麼這兩個頁面稱爲同源。一切非同源的請求均爲跨域。並跨域沒法隨意請求,只是說爲了網站的安全性,瀏覽器才採起同源策略。
若是是協議和端口形成的跨域問題,前端是無能爲力的。 跨域問題中的域,瀏覽器只是用url
首部來區分的,並不會對DNS
以後獲得的IP
進行判斷。
ps:url首部 = protocol + host;
嚴格的說,瀏覽器並非拒絕全部的跨域請求,實際上拒絕的是跨域的讀操做。瀏覽器的同源限制策略是這樣執行的:
同源策略呢,限制瞭如下行爲:
Cookie、LocalStorage、IndexDB
js
的交互操做ajax
請求發不出去(其實能夠發出去,只不過瀏覽器將響應給攔截了)跨域方式:JSONP、CORS、postMessage等
。
JSONP,JSON with Padding 參數式JSON
。 JSONP
的原理其實就是利用了<script>
標籤的src
引入外部腳本時不受同源策略的限制,經過手動添加DOM
並賦予src
請求的url
,在請求的url
中填寫接收數據的回調,再加上服務器對callback
的支持便可。
Cross-Origin Resource Sharing, CORS 跨域資源共享
。CORS
是一種web
瀏覽器的技術規範,它爲web
服務器定義了一種容許從不一樣域訪問其資源的方式。而這種跨域訪問是被同源策略所禁止的。CORS
系統定義了一種瀏覽器和服務器交互的方式來肯定是否容許跨域請求, 有更大的靈活性,比起簡單地容許這些操做來講更加安全。 CORS
須要瀏覽器和服務器共同配合、支持。整個CORS
通訊過程都是由瀏覽器來完成的,除了一些限制之外,代碼和普通的ajax
沒有什麼不一樣,實現CORS
的關鍵是服務器,只要服務器支持、實現了CORS
接口就能實現CORS
。
知足如下兩大條件的請求就是simple request
:
對於簡單請求(如下都是跨域狀況)瀏覽器直接發出CORS, 增長一個Origin頭部。
這個Origin
字段做用是告訴服務器,本次跨域請求是源自哪一個主機、端口、協議,服務器以此來判斷是否容許這次跨域。
若是Origin
不在服務器的許可範圍內,那服務器就返回正常的HTTP
響應。瀏覽器發現響應的Access-Control-Allow-Origin
和發起請求的源不相等,或者根本沒有這個字段,則瀏覽器拒絕這次請求。會被xhr
的onerror
事件捕獲,這種錯誤沒法經過狀態碼識別。
不然服務器返回的響應會多出(所謂多出,其實就是瀏覽器設置了這些頭部)等頭部信息。
能夠看到多出了'Access-Control-Allow-Credentials'、'Access-Control-Allow-Headers'
等頭部,這些頭部具體意義見下文。
服務器必須設置的值,不然不能實現CORS,它的值要麼是精確的請求的Origin,要麼是通配符*(在須要Cookie的時候不支持*)。
可選。意爲是否容許發送cookie
,默認爲不容許,不過這個字段只能設置爲true
。若是瀏覽器不容許發送cookie
,刪除該字段便可。 注意:瀏覽器在請求的時候也必須設置:xhr.withCredentials = true
;不過有的瀏覽器省略還會自動帶上cookie
,能夠手動關閉。
可選。在CORS
中,用於設置瀏覽器能夠發送的頭部。
res.setHeader('Access-Control-Allow-Headers', 'Your-Fucking-Header');
複製代碼
可選。CORS
返回請求的時候,xhr.getAllResponseHeaders();
只能拿到6個基本頭部字段:'Cache-Control'、'Content-Language'、'Content-Type'、'Expires'、'Last-Modified'、'Pragma'
。經過res.setHeader('Access-Control-Expose-Headers', 'some headers');
能夠得到容許的header
。
非簡單請求指的是那種對服務器有特殊要求的請求,如:request method
是put、delete
,或者Content-Type
爲application/json
等。 非簡單請求的CORS
請求會在正式通訊以前進行一次HTTP查詢請求,稱之爲 預檢請求(preflight
)。瀏覽器先詢問服務器,當發送方的域名在服務器容許之列,而且發送方使用的頭部、請求方法都是服務器容許的時候纔會發送正式的Ajax
請求,不然報錯。
非簡單請求除了Origin
之外,還會發送兩個特殊的頭部:'Access-Control-Request-Method','Access-Control-Request-Headers'
。
瀏覽器這次CORS會用到的HTTP方法。
指出瀏覽器會發送的額外的頭部
'Access-Control-Allow-Origin'
和
'Access-Control-Allow-Headers'
來判斷服務器是否容許
CORS
,除此以外還有如下頭部:
必需。值由','分割的String
,意爲支持的CORS
請求方法,返回的是全部支持的方法,不是瀏覽器設置的那個方法,避免屢次preflight
。
可選。單位: s(秒)。意爲本次preflight
的有效時間。在有效時間內不用再次發送預檢請求,即容許緩存該回應。
Headers | Server | Browser |
---|---|---|
Access-Control-Allow-Orgin | √ | × |
Access-Control-Allow-Headers | √ | × |
Access-Control-Allow-Methods | √ | × |
Access-Control-Max-Age | √ | × |
Access-Control-Allow-Credentials | √ | √ |
Access-Control-Expose-Headers | √ | × |
Access-Control-Request-Method | × | √ |
Access-Control-Request-Headers | × | √ |
-- | 目的 | 支持方法 | 優點 | 不足 |
---|---|---|---|---|
CORS | 跨域 | 全部HTTP請求方法 | 請求方法不只僅侷限於GET,支持全部HTTP請求方法。安全性高。 | 老版本瀏覽器不支持CORS,有必定兼容性問題,好比IE10及更早版本、Safari4及更早版本、FireFox3.5及更早版本都不支持。 |
JSONP | 跨域 | GET | 能夠向老式、不支持CORS的網站請求數據。並且設置簡單,無需設置過多的響應、請求頭部。 | ①只能支持GET方法 ②對於存在惡意行爲的服務器存在必定的安全隱患。 ③須要一個接收數據的全局函數,污染了全局做用域。 ④判斷請求是否失敗不容易(H5給script新增error事件,可是等瀏覽器實現還需以時日)。 |
若是兩個網頁的主域名相同,這個時候能夠令document.domain
都爲其主域名(document.domain
只能將其設置爲自身和更高一級的父域名)。 因爲同源限制的第二條,不一樣域的iframe
之間不能進行js
交互。因此經過iframe.contentWindow
獲取到的window
對象,它的方法和屬性幾乎都是不可用的,而且不容許獲取此window.document
。
這個時候:
document.domain = /* 兩個頁面共同的父級域名 */;
複製代碼
而後就能夠獲得iframe.contentWindow
的屬性了。也能夠經過iframe
裏面的方法請求數據,以此也能夠達到跨域的目的。
它的原理是父窗口能夠對iframe
的URL
進行讀寫,而和祖先窗口(不只僅是父窗口)同源iframe
也能夠讀寫父窗口的URL
,而hash
部分不會發送到服務器(不會產生http
請求),因此能夠經過修改hash
來實現雙向的通訊。 具體操做是:
super
窗口中有一個跨域的iframe0
,
iframe0
中又有一個和super
同源的iframe1
。
如圖所示,顏色表示是否同源。
iframe0
想要發送數據的時候,能夠直接修改iframe1
的hash
(跨域也能夠)iframe1
監聽onhashchange
事件,拿到hash
部分後,再修改super
的hash
(由於iframe1
和super
同源,因此能夠)super
也監聽onhashchange
事件,就能夠拿到數據了。 代碼以下:super:
<iframe id = "iframe" src="http://localhost:8080/iframe0.html"></iframe>
<script type="text/javascript"> let counter = 0; let url = "http://localhost:8080/iframe0.html#"; const iframe = document.getElementById('iframe'); window.onhashchange = function(event) { console.log('_我獲得數據:', event.newURL.split('#')[1]); } </script>
iframe0:
<iframe src="http://localhost/iframe1.html" frameborder="0"></iframe>
<script> let counter = 0; let url = 'http://localhost/iframe1.html#'; const iframe = document.querySelector('iframe'); setInterval(() => { console.log('我發送數據:', + counter); iframe.src = url + counter ++; }, 2000); </script>
iframe1:
<script> window.onhashchange = function() { let data = event.newURL.split('#')[1]; // 修改super的hash window.parent.parent.location.hash = data; } </script>
複製代碼
結果:
要使用postMessage
這個API
必需要有其餘窗口的引用otherWindow
發送方:
otherWindow.postMessage(data, targetOrigin, [transfer]);
複製代碼
參數說明:
data
:發送的數據targetOrigin
:指定哪些窗口接收消息,*表示任何窗口, '/'表示當前域下的窗口。transfer
:可選,和message
同時傳遞的對象,這些對象的全部權被轉移給消息的接收方,而發送方再也不擁有全部權。接收方:
window.addEventListener('message', e => {
console.log(e);
}, false);
複製代碼
在e
中有4個屬性比較重要:
data
:發送來的消息對象type
:發送消息的類型source
:發送消息的window
origin
:發送消息的origin
直接經過給e.source
添加引用類型的屬性,能夠直接給發送端的window
添加數據。其實比較經常使用的跨域方法就是CORS、JSONP,其餘的有個大概瞭解知道就行了。其餘的關於XSS、CSRF等內容回頭待續。