如何發(fā)請(qǐng)求?
用form可以發(fā)請(qǐng)求,但是會(huì)刷新頁(yè)面或新開(kāi)頁(yè)面;
用a可以發(fā)get請(qǐng)求,但是也會(huì)刷新頁(yè)面或新開(kāi)頁(yè)面;
用image可以發(fā)get請(qǐng)求,但是只能以圖片形式展示;
用link可以發(fā)get請(qǐng)求,但是只能以css、favicon的形式展示;
用script可以發(fā)get請(qǐng)求,但是只能以腳本的形式運(yùn)行。
前端需要一種方式實(shí)現(xiàn):
- get、post、put、delete等請(qǐng)求都行
- 想以什么方式展示就以什么方式展示
AJAX
AJAX(async javascript and XML) 異步的javascript和XML
滿足如下技術(shù)叫AJAX:
- 使用
XMLHttpRequest發(fā)請(qǐng)求 - 服務(wù)器返回XML格式的字符串(現(xiàn)已升級(jí)為返回JSON格式字符串)
- JS解析XML,并更新局部頁(yè)面
原生JS發(fā)送AJAX請(qǐng)求
let request = new XMLHttpRequest() //產(chǎn)生request
request.open('get','http://jack.com/xxx') //配置request
request.send() //發(fā)送request
request.onreadystatechange = () => { //監(jiān)聽(tīng)request狀態(tài)的變化
if(request.readyState === 4){ //請(qǐng)求響應(yīng)都完畢了
if(request.status >= 200 && request.status < 300){ //請(qǐng)求成功 3xx也有可能為成功,這里不考慮
let string = request.responseText
let object = window.JSON.parse(string) //把符合JSON語(yǔ)法的字符串轉(zhuǎn)換成JS對(duì)應(yīng)的值
}else if(request.status >= 400){ //請(qǐng)求失敗
}
}
}
readyState
請(qǐng)求的5種狀態(tài)
| 值 | 狀態(tài) | 描述 |
|---|---|---|
| 0 | UNSENT(未打開(kāi)) | open()方法還沒(méi)有調(diào)用 |
| 1 | OPENED(未發(fā)送) | open()方法已經(jīng)被調(diào)用 |
| 2 | HEADERS.RECEIVED(已獲取響應(yīng)頭) | send()方法已經(jīng)被調(diào)用 |
| 3 | LOADING(正在下載響應(yīng)體) | 響應(yīng)體下載中;responseText中已經(jīng)獲取了部分?jǐn)?shù)據(jù) |
| 4 | DONE(請(qǐng)求完成) | 整個(gè)請(qǐng)求過(guò)程已經(jīng)完畢 |
setInterval( () => {
console.log(request.readyState)
},1) //試試每毫秒打印一次readyState
上面代碼每毫秒打印一次readyState,會(huì)發(fā)現(xiàn)一般無(wú)法打印出所有的狀態(tài),因?yàn)檎?qǐng)求的過(guò)程太快了。
onreadystatechange
使用onreadystatechange可以監(jiān)聽(tīng)readyState的每次變化
request.onreadystatechange = () => {
console.log(request.readyState)
}
status
該請(qǐng)求的響應(yīng)狀態(tài)碼,只讀。
if(request.status >= 200 && request.status < 300){ //3xx也可能成功,此處不考慮
console.log('請(qǐng)求成功')
}else if(request.status >= 400){
console.log('請(qǐng)求失敗')
}
responseText
此次請(qǐng)求的響應(yīng)文本。
當(dāng)請(qǐng)求未成功或還未發(fā)送時(shí)為null。只讀。
JS vs JSON
JSON和JS是兩門(mén)不同的語(yǔ)言,道格拉斯(JSON作者)抄襲了JS,所以JSON很像JS。JSON官網(wǎng)
- JSON沒(méi)有function和undefined
- JSON的字符串首尾必須是雙引號(hào)
- JSON不能表示復(fù)雜對(duì)象,只能表示哈希
- JSON沒(méi)有變量
- JSON沒(méi)有原型鏈
響應(yīng)第四部分是字符串,可以是符合JSON語(yǔ)法的字符串。
//把符合JSON語(yǔ)法的字符串轉(zhuǎn)換成JS對(duì)應(yīng)的值
let string = request.responseText
let object = window.JSON.parse(string)
至此AJAX得到升級(jí),不再返回XML格式的字符串,而是返回更親近JS的JSON格式的字符串。
同源策略
只有 協(xié)議+端口+域名 一模一樣才允許發(fā)AJAX請(qǐng)求
因?yàn)锳JAX可以讀取響應(yīng)內(nèi)容,所以瀏覽器不允許一個(gè)域名的JS,在未經(jīng)允許的情況下讀取另一個(gè)域名的內(nèi)容,但是瀏覽器并不阻止你向另一個(gè)域名發(fā)請(qǐng)求。
CORS可以告訴瀏覽器,我倆是一家的,別阻止他。
在jack.com的服務(wù)器寫(xiě)上
response.setHeader('Access-Control-Allow-Origin','http://frank.com')
這樣frank.com就可以訪問(wèn)jack.com了。
CORS (Cross-Origin Resource Sharing) 跨來(lái)源資源共享。
突破同源策略 === 跨域
AJAX的所有功能
AJAX:用JS發(fā)送請(qǐng)求,用JS處理響應(yīng)。
- 客戶端JS發(fā)起請(qǐng)求(瀏覽器上的)
1. 第一部分request.open('get','./xxx')動(dòng)詞,路徑
2. 第二部分request.setRequestHeader('Content-Type','x-www-form-urlencoded')
3. 第四部分request.send('a=1&b=2') - 服務(wù)端的JS發(fā)送響應(yīng)(Node.js上的)
1. 第一部分request.status/request.statusText例如200/OK
2. 第二部分request.getResponseHeader()/request.getAllResponseHeaders()
3. 第四部分request.responseText
通過(guò)AJAX可以任意設(shè)置請(qǐng)求四部分中的所有東西(有些不安全的不讓設(shè)置),也可以獲取響應(yīng)四部分中的所有內(nèi)容。
XMLHttpRequest.setRequestHeader()是設(shè)置HTTP請(qǐng)求頭的方法,此方法必須在open()方法和send()方法之間調(diào)用。
自己封裝jQuery.ajax
window.jQuery.ajax = function(url, method, body, successFn, failFn){
//...
}
//給參數(shù)命名,有結(jié)構(gòu)的參數(shù)--對(duì)象(JS里只有對(duì)象有結(jié)構(gòu))
window.jQuery.ajax = function(options){
let url = options.url
let method = options.method
let body = options.body
let successFn = options.successFn
let failFn = options.failFn
let request = new XMLHttpRequest()
request.open(method, url)
/*for(let key in headers) {
let value = headers[key]
request.setRequestHeader(key, value)
}*/ //設(shè)置headers,下文
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)
}
//調(diào)用
let obj = {
url: '/xxx',
method: 'get',
successFn: () => {}
failFn: () => {}
}
window.jQuery.ajax(obj)
//調(diào)用可以直接寫(xiě)成
window.jQuery.ajax({
url: '/xxx',
method: 'get',
successFn: () => {}
failFn: () => {}
})
//如果請(qǐng)求成功時(shí)需要執(zhí)行兩個(gè)函數(shù)
successFn: (x) => {
f1.call(undefined,x)
f2.call(undefined,x)
}
如果需要設(shè)置headers
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'jack': '18'
}
//在open()和send()之間遍歷headers,設(shè)置請(qǐng)求頭
let headers = options.headers
for(let key in headers) {
let value = headers[key]
request.setRequestHeader(key, value)
}
jQuery.ajax有兩種傳參方式,傳一個(gè)參數(shù),或傳兩個(gè)參數(shù),第一個(gè)為url
window.jQuery.ajax = function(options){
let url
if(arguments.length === 1){ //傳一個(gè)參數(shù)
url = options.url
}else if(argument.length === 2){ //傳兩個(gè)參數(shù)
url = arguments[0] //url是第一個(gè)參數(shù)
options = arguments[1]
}
let method = options.method
let body = options.body
let successFn = options.successFn
let failFn = options.failFn
let headers = options.headers
//...
}
ES6語(yǔ)法之解構(gòu)賦值
解構(gòu)賦值語(yǔ)法是一個(gè)JavaScript表達(dá)式,這是的可以將 值從數(shù)組 或 屬性從對(duì)象 提取到不同的變量。
let url = options.url
let method = options.method
let body = options.body
let successFn = options.successFn
let failFn = options.failFn
let headers = options.headers
//上面代碼可以寫(xiě)成
let{url,mmethod,body,successFn,failFn,headers} = options
//直接從第一個(gè)參數(shù)里解構(gòu),拿到幾個(gè)變量,同時(shí)用let聲明幾個(gè)變量
window.jQuery.ajax = function({url,method,body,successFn,failFn,headers}){}
Promise
不同的庫(kù)風(fēng)格不一樣,如果不看文檔,不知道成功時(shí)傳什么參數(shù),失敗時(shí)傳什么參數(shù)。
例如我自己封裝的jQuery.ajax中,成功時(shí)傳successFn,失敗時(shí)傳failFn,與原版的jQuery是不同的。
$.ajax({
url:'/xxx',
method: 'get',
}).then(
(responseText) => {console.log(responseText)}, //成功時(shí)執(zhí)行第一個(gè)參數(shù)
(request) => {console.log'error'} //失敗時(shí)執(zhí)行第二個(gè)參數(shù)
)
上面代碼中,使用.then(),它有兩個(gè)函數(shù)作為參數(shù),成功時(shí)執(zhí)行第一個(gè)參數(shù),失敗時(shí)執(zhí)行第二個(gè)參數(shù),不用再給成功、失敗時(shí)執(zhí)行的函數(shù)命名。
.then()后面可以繼續(xù)續(xù)接.then(),可以基于上一次的處理結(jié)果繼續(xù)處理。
把自己封裝的jQuery.ajax變成promise形式
window.jQuery.ajax = function({url, method, body, headers}){
return new Promise(function(resolve, reject){
let request = new XMLHttpRequest()
request.open(method, url) // 配置request
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) //成功調(diào)用resolve
}else if(request.status >= 400){
reject.call(undefined, request) //失敗調(diào)用reject
}
}
}
request.send(body)
})
} //返回值是一個(gè)Promise實(shí)例對(duì)象,這個(gè)實(shí)例對(duì)象有then屬性;then也會(huì)返回一個(gè)Promise對(duì)象,所以可以再接.then()