如何發(fā)請求?
- 用form可以發(fā)post或get請求,但是但是會刷新頁面或新開頁面
- 用 a 可以發(fā) get 請求,但是也會刷新頁面或新開頁面
- 用 img 可以發(fā) get 請求,但是只能以圖片的形式展示
- 用 link 可以發(fā) get 請求,但是只能以 CSS、favicon 的形式展示
- 用 script 可以發(fā) get 請求,但是只能以腳本的形式運行
所以急需一種API可以滿足以下兩個要求:
- get、post、put、delete 請求都行
- 想以什么形式展示就以什么形式展示
微軟的突破
IE 5 率先在 JS 中引入 ActiveX 對象(API),使得 JS 可以直接發(fā)起 HTTP 請求。隨后 Mozilla、 Safari、 Opera 也跟進了,取名 XMLHttpRequest,并被納入 W3C 規(guī)范。
1.png
AJAX
- Jesse James Garrett 將如下技術(shù)取名叫做 AJAX(Async JavaScript And XML):異步的 JavaScript 和 XML
- 使用 XMLHttpRequest 發(fā)請求
- 服務(wù)器返回 XML 格式的字符串(現(xiàn)在都使用JSON來代替XML)
- JS 解析 XML,并更新局部頁面
- AJAX就是用JS去發(fā)請求
- 響應的第四部分是字符串,可以用 JSON 語法表示一個對象,也可以用 JSON 語法表示一個數(shù)組,還可以用 XML 語法,還可以用 HTML 語法,還可以用 CSS 語法,還可以用 JS 語法,還可以用自創(chuàng)的語法
- 示例
myButton.addEventListener('click',(e)=>{
let request = new XMLHttpRequest()
request.open('get','/xxx')//配置request
request.send()
setInterval(()=>{console.log(request.readyState)},1)
})
結(jié)果:
2.png
XMLHttpRequest.readyState 屬性返回一個 XMLHttpRequest 代理當前所處的狀態(tài)

3.png
圖2中沒有出現(xiàn)0、2、3是因為他們運行太快,沒有捕捉到;每一次都會按照0~4的狀態(tài)來運行。
由此可見,每毫秒來捕捉也無法得到所有的狀態(tài),那么有沒有一個屬性只要狀態(tài)發(fā)生變化就通知我?有的。
request.onreadystatechange
如何使用 XMLHttpRequest
myButton.addEventListener('click',(e)=>{
let request = new XMLHttpRequest()
request.open('get','/xxx')//配置request
request.send()
request.onreadystatechange = ()=>{
if(request.readyState === 4){
console.log('請求響應完畢了')
if(request.status >= 200 && request.status < 300){
console.log('說明請求成功')
console.log(request.responseText)
let string = request.responseText
let object = window.JSON.parse(string)
//把符合JSON語法的字符串轉(zhuǎn)換成JS對應的值,JSON.parse是瀏覽器提供的
console.log(typeof object)
console.log(object)
}
}
}
})
//后端代碼
else if(path==='/xxx'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001')
response.write(`
{
"note":{
"to": "小谷",
"from": "方方",
"heading": "打招呼",
"content": "hi"
}
}
`)
response.end()
}
原生JS寫一個AJAX
let request = new XMLHttpRequest()//聲明
request.open('get','/xxx')//配置request
request.send()//發(fā)送
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status < 300){
let string = request.responseText
let object = window.JSON.parse(string)//解析
}
}
}
JSON
- JSON是由Douglas Crockford(道格拉斯克羅克福德)構(gòu)想設(shè)計的數(shù)據(jù)交換語言,它是一門新的語言,與JavaScript不一樣
-
JS VS JSON4.png
同源策略
- 即只有 協(xié)議+端口+域名 一模一樣才允許發(fā) AJAX 請求,否則瀏覽器上的任何網(wǎng)頁都可被隨意盜取信息
- http://baidu.com 可以向 http://www.baidu.com 發(fā) AJAX 請求嗎? 不可以
-
http://baidu.com:80 可以向 http://baidu.com:81 發(fā) AJAX 請求嗎? 不可以
要保持一模一樣才能發(fā)AJAX請求
- 如果想要請求別的域名,那么就給它的后臺打電話,通過兩種方式處理①JSONP(但它不能發(fā)POST請求);②CORS跨域;(用哪種方法都可以)
CORS跨域
- Cross-Origin Resource Sharing(跨站資源共享)
- 只需在需要共享的那個域名下加上一句代碼
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:8001')
就是告訴瀏覽器http://frank.com:8001這個網(wǎng)站是我的朋友,你不要攔它,可以讓它讀我的內(nèi)容
AJAX的所有功能
- 客戶端的JS發(fā)起請求(瀏覽器上的)
- 服務(wù)端的JS發(fā)送響應(Node.js上的)
- JS 可以設(shè)置任意請求 header
第一部分 request.open('get', '/xxx') //瀏覽器默認get請求是不顯示第四部分的
第二部分 request.setRequestHeader('Content-Type','x-www-form-urlencoded')
第四部分 request.send('a=1&b=2') //請求體 - JS 可以獲取任意響應 header
第一部分 request.status //狀態(tài)碼
request.statusText //狀態(tài)碼的文字解釋
第二部分 request.getResponseHeader('Content-Type') //獲取指定的響應頭
request.getAllResponseHeaders() //獲取全部的響應頭
第四部分 request.responseText //響應體
至此,HTTP是如何請求響應的,就大致了解了:
①客戶端通過AJAX設(shè)置請求頭,發(fā)送請求到服務(wù)器端(服務(wù)器端需要指定端口)
②服務(wù)器端設(shè)置響應并發(fā)送到客戶端(客戶端不需要指定端口)
用jQuery封裝AJAX
window.jQuery = function(nodeOrSelector){
let nodes = {}
nodes.addClass = function(){}
return nodes
}
window.jQuery.ajax = function(url,method,body,success,fail){
let request = new XMLHttpRequest()
request.open(method,url)//配置request
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status < 300){
success.call(undefined,request.responseText)
}else if(request.status >= 400){
fail.call(undefined,request)
}
}
}
request.send(body)
}
window.$ = window.jQuery
myButton.addEventListener('click',(e)=>{
window.jQuery.ajax(
'/xxx',
'post',
'a=1&&b=2',
()=>{console.log(1)},
()=>{console.log(2)}
)
})
但是上面最大的不足就是ajax函數(shù)中參數(shù)太多,容易造成忘記每個參數(shù)代表什么,所以需要作出優(yōu)化,把所有參數(shù)轉(zhuǎn)換成一個對象傳進去
window.jQuery.ajax = function(options){
let url = options.url
let method = options.method
let body = options.body
let success = options.success
let fail = options.fail
let headers = options.headers
......
}
利用ES6的解構(gòu)賦值語法優(yōu)化參數(shù)
window.jQuery.ajax = function(options){
let {url,method,body,success,fail,headers} = options
......
}
繼續(xù)優(yōu)化
window.jQuery.ajax = function({url,method,body,success,fail,headers}){
......
}
Promise
- 因為成功需要回調(diào)成功函數(shù),失敗要回調(diào)失敗函數(shù),但是每個庫的成功失敗回調(diào)函數(shù)名是不一樣的,例如image.onload(成功),image.onerror(失?。?,我們不可能一一地去查每個庫回調(diào)函數(shù)的名字,所以就引入promise(promise是window下的全局屬性),利用promise的then屬性。
then(()=>{},()=>{}) //成功執(zhí)行第一個函數(shù),失敗執(zhí)行第二個函數(shù)
- 利用jQuery的promise屬性(不要忘記引入jQuery)
myButton.addEventListener('click',(e)=>{
$.ajax(
{url:'/xxx',
method:'post',
headers:{
'Content-Type':'x-www-form-urlencoded',
'doudou':'18'
}
}).then(
(text)=>{console.log(text)},
(request)=>{console.log(request)}
)
})
- 自己定義promise來封裝AJAX
window.jQuery = function(nodeOrSelector){
let nodes = {}
nodes.addClass = function(){}
nodes.html = function(){}
return nodes
}
window.$ = window.jQuery
window.Promise = function(fn){
return {
then: function(){}
}
}
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)
}else if(request.status >= 400){
reject.call(undefined,request)
}
}
}
request.send(body)
})
}
myButton.addEventListener('click',(e)=>{
let promise = window.jQuery.ajax(
{url:'/xxx',
method:'post',
headers:{
'Content-Type':'x-www-form-urlencoded',
'doudou':'18'
}
})
promise.then(
(text)=>{console.log(text)},
(request)=>{console.log(request)}
)
})
