XMLHttpRequest
JSON
AJAX
CORS
四個名詞來開會
在前端的世界裏也逛蕩了很多日子了,目前已經get到大約5種發起請求的方式,主流的、非主流的。javascript
何種方式 | 請求方法 | |
---|---|---|
最多見的form 表單 |
默認GET ,多用POST ,只此兩種 |
會刷新頁面或者新開頁面 |
a 標籤 |
GET 請求 |
也會刷新頁面或者新開頁面 |
img 的src 屬性 |
GET |
只能以圖片的形式展示 |
link 標籤 |
GET |
只能以CSS 、favicon 的形式展示 |
script 標籤 |
GET |
只能以腳本的形式運行 |
但是html
GET
POST
PUT
DELETE
方法在AJAX
未出現以前,瀏覽器想從服務器得到資源,注意是獲取資源,會通過以下一個過程前端
我稱這種交互方式是 V1.0,此時仍是以獲取資源爲導向。後來隨着時代的發展,人們日益增加的文化需求成爲了社會的主要矛盾……有一天,小明看了一篇報道,他只是想在下面評論一下,發表對實事的親切問候,問候完了,唉,你給我刷新頁面幹啥,我只是想評論一下啊。java
大概那是網民們第一次對 良好的用戶體驗 提出了要求。後來的蘋果爸爸,把你們慣壞了,每天嚷着 "你這產品用戶體驗太差了"……node
彼時,微軟仍是對web作出了很大的貢獻的。git
大約1999年,微軟發佈IE 5.0
版本,它容許JavaScript腳本向服務器發起HTTP請求。不過很遺憾,當時也沒有火起來,直到2004年Gmail發佈和2005年Google Map發佈,才引發普遍重視。2005年,一個叫Jesse James Garrett的人提出了一個新術語----AJAX
,它是一系列技術的組合體,全稱是 Asynchronous JavaScript + XML
(異步的JS和XML)能夠阻止頁面總體刷新,只是動態響應用戶的操做,快速顯示到局部,用戶就能夠很愉快的繼續上網了。程序員
AJAXes6
能夠看出IE當時仍是很猛的,隨着IE 6.0 市場份額進一步擴大,IE已經把火狐整的半死不活,放眼整個瀏覽器市場,微軟是當之無愧的王者,後來微軟就把瀏覽器團隊解散了……不得不說這是一波神操做,能與之媲美的操做大概只有殘血我能反殺
塔下我能秀他
了。微軟強行爲後續各家瀏覽器的發展提供了優秀的工程師,尤爲是0八、09年出生的谷歌瀏覽器,再看現在的IE……github
既然AJAX
是一系列的技術的組合體,接下來認識一下其中的幾位主角web
XMLHttpRequest
對象是用來在瀏覽器和服務器之間傳輸數據的。
古代的操做的是:
XMLHttpRequest
實例化對象XML
格式的字符串,是字符串,是字符串,是字符串,也就是說響應的第四部分是字符串。什麼是XML,可擴展標記語言。
以上是最初的用法,用的是XML
,前端代碼片斷以下
let request = new XMLHttpRequest() //實例化XMLHttpRequest對象 request.onreadystatechange = () => { if (request.readyState === 4) { console.log('請求和響應都完畢了') if (request.status >= 200 && request.status <= 300) { console.log('說明請求成功了') console.log(request.responseText) let parser = new DOMParser() let xmlDoc = parser.parseFromString(request.responseText, "text/xml") //用parser解析request.responseText let c = xmlDoc.getElementsByTagName('body')[0].textContent console.log(c) } else if (request.status >= 400) { console.log('說明請求失敗了') } } } request.open('GET', '/xxx') //配置request request.send()
服務器端的對應代碼片斷以下
... response.statusCode = 200 response.setHeader('Content-Type', 'text/xml;charset=utf-8') response.write(` <note> <to>木木</to> <from>少少</from> <heading>你好哇</heading> <body>很久不見啊</body> </note> `) response.end() ...
本地模擬的話,必定要記得開倆不一樣的端口
例如:
node server.js 8001 node server.js 8002
正如上面的前端代碼片斷寫的同樣,主要用到了open()
send()
方法, onreadystatechange
readyState
屬性。
request.open(method, URL, async)方法。
GET POST DELETE PUT
等等,URL是用訪問的路徑,async是是否使用同步,默認true,開啓異步,不須要作修改便可,因此實際中只寫前兩個參數request.send()方法。
readyState
屬性。
描述請求的五個狀態。
UNSENT
(未打開) open()方法未調用OPENED
(未發送) 只是open()方法調用了HEADERS_RECEIVED (已獲取響應頭)
send()方法調用了,響應頭和響應狀態已經返回了LOADING (正在下載響應體)
響應體下載中,responseText
已經獲取了部分數據DONE (請求完成)
整個響應過程完畢了。 這個值是實際中用到的。 responseText
屬性是這次響應的文本內容。onreadystatechange
屬性。
readyState
屬性的值發生改變,就會觸發readyStateChange
事件。onReadyStateChange
屬性,指定這個事件的回調函數,對不一樣狀態進行不一樣處理。尤爲是當狀態變爲4的時候,表示通訊成功,這時回調函數就能夠處理服務器傳送回來的數據。即前面的代碼片斷的處理方式。習慣用javaScript
的前端是不想和XML
打交道的,應該用一種符合js
風格的數據格式語言。
後來一個美國程序員道格拉斯·克羅克福特發明了JSON
,解決了上面的問題,這貨還寫了一本蝴蝶書JavaScript語言精粹,還發明瞭一個JS校驗器 ----JSLint。
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON採用徹底獨立於語言的文本格式,可是也使用了相似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成爲理想的數據交換語言。
以上是JSON官網的簡介,能夠看出它是一門全新的語言,不是JavaScript的子集。
JSON
很簡單,數據類型和JS有點不一樣的地方。JavaScript | JSON |
---|---|
string | "string" 必須寫雙引號 |
number | number |
object | {"object": "name"} 必須雙引號 |
undefined | 沒有 |
null | null |
boolean | 直接寫true false |
array | array |
function | 沒有 |
variable |
window
上有JSON
對象,直接使用window.JSON.parse(string)
let string = request.responseText let json = window.JSON.parse(string) //string 要符合JSON的格式
以上是JSON解析部分的代碼。
此時服務器端代碼是
response.statusCode = 200 response.setHeader('Content-Type', 'text/json;charset=utf-8') response.write(` { "note" : { "to" : "木木", "from" : "少少", "heading" : "你好哇", "content" : "很久不見啊" } } `)
4.AJAX
剛好是同源政策的擁躉
若是AJAX
向非同源的地址發起請求,會報錯。
<button id="myButton">點我</button> <form action="https://www.baidu.com" method="get"> <input type="password" name="password"> <input type="submit" value="提交"> </form>
上述請求響應都沒有問題
然而對於AJAX
就不行
... request.open('GET', 'http://www.baidu.com') ...
原頁面用 form 提交到另外一個域名以後,原頁面的腳本沒法獲取新頁面中的內容,因此瀏覽器認爲這是安全的。
而 AJAX 是能夠讀取響應內容的,所以瀏覽器不能容許你這樣作。若是你細心的話你會發現,其實請求已經發送出去了,你只是拿不到響應而已。
因此瀏覽器這個策略的本質是,一個域名的 JS ,在未經容許的狀況下,不得讀取另外一個域名的內容。但瀏覽器並不阻止你向另外一個域名發送請求。做者:方應杭
連接:https://www.zhihu.com/questio...
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
那麼如何讓AJAX
跨域發起請求呢。
答案是CORS
CORS
目前是W3C的標準,它容許瀏覽器跨域發起XMLHttpRequest
請求,並且能夠發起多種請求,不像JSONP
只能發起GET
請求,全稱是"跨域/源資源共享"(Cross-origin resource sharing)。
request.open('GET', 'http://wushao.com:8001/xxx') //配置request
response.setHeader('Access-Control-Allow-Origin', 'http://shaolin.com:8002')
必定要注意是誰去訪問誰,8001去訪問8002,那麼8001的前端代碼要告訴8002的後端代碼,我們是一家人,你和瀏覽器說說別讓它禁我了。
既然能夠發請求,那麼請求頭的四部分如何得到的,響應的四部分又是如何得到呢
request.open('GET', 'http://shaolin.com:8002/xxx')// 請求的第一部分 request.setRequestHeader('Content-Type', 'x-www-form-urlencoded')//請求的第二部分 request.setRequestHeader('wushao', '18') //請求的第二部分 request.send('我要設置請求的第四部分') //請求的第四部分 request.send('name=wushao&password=wushao') //請求的第四部分
對應的典型的http請求四部分
GET /xxx HTTP/1.1 HOST: http://shaolin.com:8002 Content-Type: x-www-form-urlencoded wushao: 18 name=wushao&password=wushao
request.status //響應的第一部分 200 request.statusText //響應的第一部分 OK request.getAllResponseHeaders //響應的第二部分,這個方法好啊,所有的響應頭 request.getResponseHeader('Content-Type') //響應的第二部分具體的 request.responseText //響應的第四部分
對應的典型的http響應的四部分
HTTP/1.1 200 OK Content-Type: text/json;charset=utf-8 { "note" : { "to" : "木木", "from" : "少少", "heading" : "你好哇", "content" : "很久不見啊" } }
回顧一下各個status對應的意思
100 200 === OK,請求成功 301 === 被請求的資源已永久移動到新位置 302 === 請求臨時重定向,要求客戶端執行臨時重定向 304 === 和上次請求同樣,未改變 403 === 服務器已經理解請求,可是拒絕訪問 404 === 請求失敗,服務器上沒有這個資源 502 === 做爲網關或者代理工做的服務器嘗試執行請求時,從上游服務器接收到無效的響應。 503 === Service Unavailable 因爲臨時的服務器維護或者過載,服務器當前沒法處理請求。
這是一個很簡陋的效果,首先我仍是把jq假設的很簡單,就是一個window的屬性,請輕噴……
window.jQuery = function (nodeOrSelector) { let nodes = {} nodes.addClass = function () {} nodes.html = function () {} return nodes } window.jQuery.ajax = function (options) { let url = options.url let method = options.method let headers = options.headers let body = options.body let successFn = options.successFn let failFn = options.failFn let request = new XMLHttpRequest() //實例化XMLHttpRequest對象 request.open(method, url) for (let key in headers) { let value = headers[key] request.setRequestHeader(key, value) } request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status >= 200 && request.status <= 300) { successFn.call(undefined, request.responseText) } else if (request.status >= 400) { failFn.call(undefined, request) } } } request.send(body) }
以上就是jq對ajax的簡陋的封裝,ajax()方法接受一個對象做爲參數,這個對象有不少鍵。這些鍵就是http請求的頭的各個部分,以及一個成功函數和一個失敗函數。
myButton.addEventListener('click', (e) => { window.jQuery.ajax ({ url: '/xxx', method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded', 'wushao': '18' }, body: 'a=1&b=6', successFn: (x) => { ... }, failFn: (x) => { ... } }) })
以上就是簡化後的使用方法,給button綁定事件的時候,函數體直接就是ajax()
let url if (arguments.length === 1) { url = options.url } else if (arguments.length === 2) { url = arguments[0] options = arguments[1] } let method = options.method let headers = options.headers let body = options.body let successFn = options.successFn let failFn = options.failFn
加了一點,判斷ajax()的參數個數。
//Promise這個對象呢,大概長這個樣子,真實面目我是沒見過 //簡單的寫一下promise window.Promise = function (fn) { //...一些其餘代碼 return { then: function () {} } }
Promise這個構造函數呢,又會返回一個函數,這個返回的函數一個then屬性,value又是一個函數。到處都體現着函數是第一公民的地位!!!
那咱們能夠利用這個強大的Promise對象搞一些事情了。
//第一步的代碼改形成這樣,第一步用到了ES6的解構賦值法 window.jQuery.ajax = function ({url, method, body, headers}) { return new Promise(function (resolve, reject) { let request = new XMLHttpRequest() request.open(method, url) for(let key in headers) { let value = headers[key] request.setRequestHeader(key, value) } request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status >= 200 && request.status <= 300) { resolve.call(undefined, request.responseText) } else if (request.status >= 400) { reject.call(undefined, request) } } } request.send(body) }) }
關於解構賦值:ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)
詳見ES6解構賦值
//通過上面這麼一折騰,能夠很簡單的使用了 myButton.addEventListener('click', (e) => { let promise = window.jQuery.ajax({ url: '/xxx', method: 'get', headers: { 'content-type': 'application/x-www-form-urlencoded', 'wushao': '18' } }) promise.then( (responseText) => { console.log(responseText) }, (request) => { console.log(request) } ) })
注意then能夠傳入兩個函數,第一個函數表示成功了執行這個,第二個函數表示失敗了執行這個,並且能夠進行鏈式調用,一直點下去。
myButton.addEventListener('click', (e) => { $.ajax({ url: '/xxx', type: 'GET', }).then( (responseText) => { console.log(responseText) return responseText }, (request) => { console.log('error') return '已經處理' } ).then( (responseText) => { console.log(responseText) }, (request) => { console.log(error2) } ) })
鏈式調用的意思就是:成功函數成功了,就執行第二個then的第一個函數;成功函數失敗了,就執行第二個then的第二個函數。
完整代碼詳見個人gitHub