AJAX

可以如何發送請求

  • <form>可以發請求,但是會刷新頁面或新開頁面

    <form action='xxx' method='get'>
        <input type='password' name='password'>
        <input type='submit'>
    </form>
  • <a>只可以發 get 請求,但是也會刷新頁面或新開頁面
  • <img>只可以發 get 請求,但是隻能以圖片的形式展示
  • <link>只可以發 get 請求,但是隻能以 CSS、favicon 的形式展示
  • <script>只可以發 get 請求,但是隻能以腳本的形式運行

❓有沒有什麼方式可以實現:get、post、put、delete 請求都行,想以什麼形式展示就以什麼形式展示❓

ajax

Asynchronous Javascript And XML
異步 Javascript 和 XML

需要滿足下面三個條件,可以稱之爲 ajax:

  1. 使用 XMLHttpRequest 發請求
  2. 服務器返回 XML 格式的字符串
  3. JS 解析 XML,並更新局部頁面

不過隨着技術發展,XML 越來越不常用,經常使用 JSON 代替 XML

version1.0

我們嘗試做一個按鈕,點擊向服務器發送一個請求

html :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax_html</title>
</head>
<body>
    <button id='myButton'>點我</button>
</body>
<script src='./main.js'></script>
</html>

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建請求
    request.onreadystatuschange=function(){    //當請求的狀態有變化時,打印出狀態碼
        console.log(request.readyStatus);
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路徑
    request.send();                            //發送請求
})

後端代碼 :

}else if(path==='/xxx'){
    response.statusCode = 200
    response.setHeader('Content-Type', 'text/xml;charset=utf-8')
    response.write(`
        //xml
        <note>
          <to>Tove</to>
          <from>Jani</from>
          <heading>Reminder</heading>
          <body>Don't forget me this weekend!</body>
        </note>
    `)
    response.end()
}

效果 :
當點擊 點我 按鈕時,首先,新建一個XMLHttpRequest請求;其次,使用 GET 方法請求到 /xxx;最後,發送請求。

當服務器收到請求了 /xxx 路徑,然後,就返回了一串格式符合 XML字符串

並且,當請求和響應進行到各個狀態時,都會打印出它的狀態碼
(狀態碼請參考:https://developer.mozilla.org...
返回xml

version2.0-獲取xml

除了可以獲取狀態碼、在瀏覽器控制檯獲取服務器返回的XML字符串外,還可以將XML字符串轉換爲XML
並且可以使用DOM獲取元素的API,來獲取XML的元素,例如getElementByTagNames...
修改後的 main.js 如下所示,新增內容和註釋如👈👇所指。

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建請求
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         👈//表示請求響應都已經完成
            if(request.status === 200){        👈//表示服務器返回成功
                👇//下面三句 let 用來獲取返回的 xml,而不再是之前字符串格式
                let parser = new DOMParser();
                👇//返回的內容可以用 responseText 獲取
                let xmlDoc = parser.parseFromString(request.responseText,'text/xml');
                let title = xmlDoc.getElementsByTagNames('heading')[0].textContent;
            }
        }
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路徑
    request.send();                            //發送請求
})

version3.0-JSON

由於 xml 看上去不夠簡潔,而且實際使用中,獲取的方式比較繁瑣(參考上面的三句 let),所以,發明了一個新的數據格式化語言——JSON,用以替代 xml。
(JSON參考官網:http://www.json.org/

JSON vs. Javascript:

  1. 沒有 functionundefined
  2. 字符串首尾必須是雙引號 ""
  3. 沒有 變量
  4. 沒有 原型鏈

修改後端代碼,使其返回一個JSON,👈👇爲修改處。

後端代碼 :

}else if(path==='/xxx'){
    response.statusCode = 200
    response.setHeader('Content-Type', 'text/json;charset=utf-8')    //👈xml修改成json
    response.write(`
        //json            //👈👇返回json而不是xml
        {
            "note":{
                "to":"Tove",
                "from":"Jani",
                "heading":"Reminder",
                "body":"Don`t forget me this weekend!"
            }
        }
    `)
    response.end()
}

由於服務器返回的只能是一個字符串(響應第4部分只能是字符串),所以,我們也需要將返回的JSON字符串轉換爲一個對象,方便操作。

接下來,可以使用js對對象的操作,獲取json的內容。

修改後的前端代碼,👈爲修改處。

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建請求
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         //表示請求響應都已經完成
            if(request.status === 200){        //表示服務器返回成功
                console.log("請求成功");
                let string=request.responseText;            👈//獲取服務器返回的字符串
                let jsonObject=window.JSON.parse(string);   👈//將返回的字符串變成對象
                console.log(jsonObject.note);               👈//輸出對象裏的note
                console.log(jsonObject.note.from);          👈//輸出對象裏的note裏的from
            }
        }
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路徑
    request.send();                            //發送請求
})

同源策略&CORS跨域

<form>表單提交內容不一樣,AJAX不刷新頁面,並且可以讀取響應內容,所以被瀏覽器認爲是不安全的,
所以:

只有 協議+域名+端口 一模一樣的時候,才允許發送AJAX請求

Σ(っ °Д °;)っ
那要是我必須需要另一個網站的接口怎麼辦???

如果http://www.kitty.com想要訪問http://www.ben.com的一個接口,那Ben只需要在後端代碼里加上一句話,告訴瀏覽器,http://www.kitty.comhttp://www.ben.com是友好關係,可以互相訪問。

Ben的後端代碼:

}else if(path==='/xxx'){           //請求的接口
    response.statusCode = 200      //返回狀態碼
    response.setHeader('Content-Type', 'text/json;charset=utf-8')    //設置返回類型爲json
    👇//此句開啓友好模式
    response.setHeader('Access-Control-Allow-Origin', 'http://www.kitty.com')
    response.write(`
        //json
        {
            "note":{
                "to":"Tove",
                "from":"Jani",
                "heading":"Reminder",
                "body":"Don`t forget me this weekend!"
            }
        }
    `)
    response.end()
}

添加response.setHeader('Access-Control-Allow-Origin', 'http://www.kitty.com'),瀏覽器就知道,Kitty和Ben是好朋友,可以互相訪問啦!!!

上述方法稱之爲:
Cross-Origin Resource SharingCORS

使用Javascript發送任意格式的請求

AJAX還允許用戶使用Javascript設置一個請求的任意部分,並且可以獲取一個響應的任意部分。

一個請求:

GET /xxx HTTP/1.1    //第一部分,方法、地址、協議、協議版本號
HOST:                //第二部分,包含很多行,包含很多內容
Content-Type:        //第二部分,包含很多行,包含很多內容
                     //第三部分,一個回車
這裏是請求體          //第四部分

一個響應:

Http/1.1 200 OK      //第一部分,協議、協議版本號、狀態碼、狀態信息
Content-Type:        //第二部分,
                     //第三部分,一個回車
這裏是響應體          //第四部分

發送一個任意的請求&獲取一個響應任意部分:

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();            //新建請求
    request.open('GET','/xxx');                   //設置請求第一部分:方法、路徑
    request.setRequestHeader('Content-Type','x-www-form-urlencoded);    //設置請求第二部分
    request.send('雖然我是GET,但我也有請求體');    //設置請求第四部分
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         //表示請求響應都已經完成
            if(request.status === 200){        //表示服務器返回成功
                console.log("請求成功");
                console.log(request.status)        //獲取響應第一部分,狀態碼
                console.log(request.statusText)    //獲取響應第一部分,狀態信息
                console/log(request.getResponseHeader('Content-Type'))    //獲取響應第二部分
                console.log(request.getAllResponseHeaders);      //獲取所有響應第二部分
                console.log(request.responseText);               //獲取響應第四部分
            }
        }
    }
})

總結:
總之,就是AJAX允許用戶使用Javascript設置任意一個請求(瀏覽器不讓設置的內容不能設置),也能獲取一個響應的任意部分。

使用 JS 設置任意請求 header

  • 第一部分:request.open('get','/xxx')
  • 第二部分:request.setHeader('Content-type','x-www-form-urlencoded')
  • 第四部分:request.send('a=1&b=2')

使用 JS 獲取任意響應 header

  • 第一部分:request.status / request.statusText
  • 第二部分:request.getResponseHeader() / request.getAllResponseHeaders()
  • 第四部分:request.responseText

客戶端與服務器

封裝ajax

我們的目標是隻使用一個函數,就能實現下面發送請求的功能:

  • request.open('GET','/xxx');
  • request.onreadystatuschange=function(){}
  • request.send('雖然我是GET,但我也有請求體');

version1.0

封裝後的前端代碼:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(url,method,body,successFn,failFn){
    var request = new XMLHttpRequest();            //新建請求
    request.open(method,url);                     👈//傳入發送請求方式、請求地址
    request.send(body);                           👈//傳入請求體
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         //表示請求響應都已經完成
            if(request.status === 200){        //表示服務器返回成功
                successFn.call(undefined,request.responseText);  👈//響應成功,調用傳入的成功函數
            }else if(request.status >= 400){
                failFn.call(undefined,request);   👈//相應失敗,調用傳入的失敗函數
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        '/xxx',
        'GET',
        '雖然我是GET,但我也有請求體'
        function(){},
        function(){}
    )
})

實現的功能是一樣的,只是將ajax封裝在一個函數裏,之後調用只需要調用一個函數,出入響應的參數就能實現發送請求的功能。

version2.0

上個版本封裝的很好,但是有幾個問題:

  • 需要傳入的參數太多,可能是不是會忘了需要傳入什麼參數,無法快速的知道參數含義
  • 當有一個參數用戶不想傳入的時候,只能使用null進行佔位,很麻煩

所以,我們想到,給它傳入一個集成的參數,把需要的參數都放在一個對象裏,這樣可以給參數指定名字,調用時,可以調用對象裏的名字。

修改封裝後的前端代碼:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(option){    👈//由原來的傳入一個個的參數,變爲傳入包含所有參數的對象
    👇//首先獲取參數對象裏的各個值
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;

    var request = new XMLHttpRequest();            //新建請求
    request.open(method,url);                      //傳入發送請求方式、請求地址
    request.send(body);                            //傳入請求體
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         //表示請求響應都已經完成
            if(request.status === 200){        //表示服務器返回成功
                successFn.call(undefined,request.responseText);   //響應成功,調用傳入的成功函數
            }else if(request.status >= 400){
                failFn.call(undefined,request);    //相應失敗,調用傳入的失敗函數
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        👇//由原來的傳入一個個的參數,變成現在傳入一個包含所有參數的對象
        {
            url: '/xxx',
            method: 'GET',
            body: '雖然我是GET,但我也有請求體'
            successFn: function(){},
            failFn: function(){}
        }
    )
})

version3.0

我們還需要封裝發送請求頭:

再次修改封裝後的前端代碼:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(option){    //由原來的傳入一個個的參數,變爲傳入包含所有參數的對象
    //首先獲取參數對象裏的各個值
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;
    let headers=option.headers;    👈//獲取參數對象裏的headers
    
    👇//遍歷headers,將裏面的鍵、值都設置在請求頭裏
    for(let key in headers){
        let value=headers[key];
        request.setRequestHeader(key,value);
    }

    var request = new XMLHttpRequest();            //新建請求
    request.open(method,url);                      //傳入發送請求方式、請求地址
    request.send(body);                            //傳入請求體
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
        if(request.readyStatus === 4){         //表示請求響應都已經完成
            if(request.status === 200){        //表示服務器返回成功
                successFn.call(undefined,request.responseText);   //響應成功,調用傳入的成功函數
            }else if(request.status >= 400){
                failFn.call(undefined,request);    //相應失敗,調用傳入的失敗函數
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        👇//在參數對象裏傳入headers,裏面包括了想要設置的請求頭的鍵、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '雖然我是GET,但我也有請求體'
            successFn: function(){},
            failFn: function(){}
        }
    )
})

運用jQuery使用ajax

請參考jQuery文檔:https://api.jquery.com/jQuery...
jquery_ajax

題外話

計算代碼執行時間

下列代碼可以計算從console.time()console.timeEnd()之間代碼執行的時間

console.time()
    //your code
console.timeEnd()

結構化編程:

  • 順序執行
  • if...else...
  • while/for

ES6解構賦值

使用ES6解構賦值新語法可以簡化下列代碼:

window.jquery.ajax=function(option){
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;
    let headers=option.headers; 
    
    //your code
}

解構之後:

window.jquery.ajax=function(option){
    let {url,method,body,successFn,failFn,headers}=option;
    
    //your code
}

更加高級點:

window.jquery.ajax=function({url,method,body,successFn,failFn,headers}){
    
    //your code
}

👆直接從window.jquery.ajax=function(){}的第一個參數裏解構,並用let聲明!!!

version4.0-Promise

當用戶要使用很多ajax的時候,用i定義的或者別人自定義的,這樣,每個人ajax的接口名稱都不一樣,用起來很麻煩,這時候,我們可以使用Promise技術

使用Promise和then:
👆👇爲修改處

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function({url,method,body,headers}){    
                                👆//使用ES6解構新語法,並且刪除了successFn、failFn
                                
    👇//promise和之後使用ajax時的then一起,可以自動判斷成功函數和失敗函數
    return new Promise(function(resolve,reject){ 
    
        //遍歷headers,將裏面的鍵、值都設置在請求頭裏
        for(let key in headers){
            let value=headers[key];
            request.setRequestHeader(key,value);
        }
    
        var request = new XMLHttpRequest();            //新建請求
        request.open(method,url);                      //傳入發送請求方式、請求地址
        request.send(body);                            //傳入請求體
        request.onreadystatuschange=function(){    
            console.log(request.readyStatus);      //當請求的狀態有變化時,打印出狀態碼
            if(request.readyStatus === 4){         //表示請求響應都已經完成
                if(request.status === 200){        //表示服務器返回成功
                    👇//響應成功,調用傳入的成功函數,修改sucessFn爲resolve
                    resolve.call(undefined,request.responseText); 
                }else if(request.status >= 400){
                    👇//響應成功,調用傳入的成功函數,修改failFn爲reject
                    reject.call(undefined,request);    //相應失敗,調用傳入的失敗函數
                }
            }
        }
    })
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        //在參數對象裏傳入headers,裏面包括了想要設置的請求頭的鍵、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '雖然我是GET,但我也有請求體'
        }
    ).then(function(){},function(){})    
    👆//這就是promise,就是在ajax後面加上then,
    👆//傳入第一個函數會被當成是成功時調用的函數,第二個函數會被當成失敗時調用的函數
})

如果需要多次處理:

//your code

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        //在參數對象裏傳入headers,裏面包括了想要設置的請求頭的鍵、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '雖然我是GET,但我也有請求體'
        }
    ).then(
        function(){
            //your code
            return xxx;
        },
        function(){})
     .then(
         function(){},
         function(){})    
    👆//這就是promise,就是在ajax後面加上then,
    👆//傳入第一個函數會被當成是成功時調用的函數,第二個函數會被當成失敗時調用的函數
    👆//當需要多次調用,可以在then的函數裏直接return,return的內容會被當成之後then的輸入,直接處理
})
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章