AJAX知識總結

Ajax簡介

Ajax(Asynchronous JavaScript And XML),可以通過其在瀏覽器中向服務器發送異步請求,無需刷整個頁面就可以獲取數據
Ajax不是新的編程語言,而是一種將現在的標準組合在一起使用的新方式

使用場景:實現懶加載,當需要展示某一數據時再去請求數據
優點:

  • 無需刷新整個頁面就可以與服務器端進行通信
  • 允許根據用戶時間來更新部分頁面內容

缺點:

  • 沒有瀏覽歷史,不能回退
  • 存在跨域問題
  • SEO不友好:使用ajax請求的數據,是異步請求的方式。SEO主要是獲取源文檔內容,也就是響應體內的內容,但是響應體內的內容需要在解析的過程中動態去獲取的,所以SEO無法獲取通過ajax請求的數據,也即通過ajax展示的數據對SEO不友好。

XML

XML(可擴展標記語言):被設計用來傳輸和存儲數據。XML和HTML類似,不同的是HTML中都是預定義標簽,而XML都是自定義標簽,用來表示一些數據。但是目前大部分使用JSON來替代XML,原因:JSON數據更容易書寫,并且轉換也更方便。

HTTP

HTTP(hypertext transport protocol)協議,超文本傳輸協議,協議詳細規定了瀏覽器和萬維網服務器之間互相通信的規則。主要規定了請求和響應規則。

格式:

  • 請求報文
行 POST  /getNames HTTP/1.1 (分別是:請求方式/請求資源路徑/HTTP版本號)

頭 Host: xxx.xxx.xxx
    Connection: keep-alive 
    Pragma: no-cache Cache-Control: no-cache
    Accept-Encoding: gzip, deflate, br(以上都是瀏覽器告訴服務器想要請求什么樣的數據,以及想要跟服務器進行怎樣的約定)
 
 空行
 
 體  username=admin&password=admin(如果是post請求則請求體有數據)
 
  • 響應報文
行 HTTP/1.1  200 OK (分別是:HTTP版本號/響應狀態碼/響應狀態碼的描述)

頭 
    cache-control: no-cache
    content-encoding:br
    content-type: application/json; charset=utf-8(以上都是服務器告訴瀏覽器響應的數據相關的信息)
 
 空行
 
 體  響應數據

Ajax使用

ajax基本使用

// 獲取xmlhttprequest實例
        const xhr = new XMLHttpRequest();
        // 初始化
        xhr.open('GET','http://127.0.0.1:8000/server');
        // 發送請求 如果部需要發送請求體,則必須傳null,因為該參數在一些瀏覽器中是必須的。
        xhr.send(null);
        // 事件綁定,處理響應結果,每次readyState數據發生變化都會觸發onreadystatechange事件。
        xhr.onreadystatechange = function () {
        // readyState表示當前處在請求/響應的哪個階段
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    // 響應行
                    console.log(xhr.status);// 狀態碼
                    console.log(xhr.statusText);//狀態碼字符串
                    // 所有的響應頭
                    console.log(xhr.getAllResponseHeaders());
                    // 響應體
                    console.log(xhr.response);
                    // or 響應體的字符串表示,該方式不受reponseType設置影響
                    console.log(xhr.responseText);
                }
            }
        }

readyState的屬性值有:

  • 0:未初始化。尚未調用open方法。
  • 1:已打開(open)。已調用open方法,但是還沒有調用send()方法
  • 2:已發送(send)。已調用send方法,尚未收到響應。
  • 3:接受中。已收到部分響應。
  • 4:完成。已收到全部的響應。

所以可以看出,一般通過判斷readyState是否為4從而判斷是否獲取到了響應數據。

以上基本使用中用到了open方法作為請求的初始化操作。那么open方法如何去使用呢?

xhr.open('GET', 'http://127.0.0.1',true);

第一個參數代表請求的方式,一般有:

  • GET
  • POST

第二個參數代表請求的URL,如果是GET方式請求,當要傳輸查詢字符串的時候,則需要在URL后面進行拼接
例如:

/test?uname=admin&password=admin

第三個參數表示是否使用異步請求,默認為true。如果為false,則是同步請求,會導致阻塞。

初始化完成之后,可以使用send方法發送數據。

  • GET方法,由于該方法傳輸查詢參數是通過拼接字符串的形式,所以當使用send方法的時候,不需要傳入任何數據,但是有些瀏覽器需要該參數,因此最好傳一個null。
  • POST方法,可以使用該方法傳輸任何數據,該數據作為請求體進行傳輸。

send方法可以不寫么?不可以,原因:open僅僅初始化了請求,但是并沒有發送請求,而發送請求的真正時間點,在send方法調用之后。

設置請求頭

可以使用setRequestHeader設置預定義請求頭和自定義請求頭

xhr.setRequestHeader('uname','admin');// 自定義請求頭

注意:自定義請求頭可以會觸發預檢請求。

獲取json數據

當服務器端返回的數據是json字符串數據的時候,那客戶端如何獲取json數據本身呢?

  1. 手動轉換

利用JSON.parse(jsonstr)來進行轉換

  1. 配置,自動轉換

XMLHttpRequest實例對象上有一個屬性:responseType,可以通過這個屬性對返回的數據進行自動轉換,這里需要賦值為json,即:xhr.responseType = 'json'這樣,獲取到的響應體數據會自動轉換為json格式,但是需要注意的是,只有response這個屬性數據才會受responseType數據設置的影響。responseText本身就是對響應體的字符串表示,所以不受影響。

超時和網絡異常處理

  • 對于請求超時,客戶端可以這樣設置
xhr.timeout = 2000;

表示,如果2s中還未收到響應,則作為超時處理,此時會取消請求。
例如:


Image.png

一般對于請求超時,客戶端都會通知用戶,類似的,這里可以使用超時回調,來進行超時的一個處理:

xhr.ontimeout = function() {
   alert('網絡超時');
}
  • 對于網絡異常/斷網的情況下,也有對應的回調函數供開發人員去操作
xhr.onerror = function() {
  alert('哦豁,網絡異常');
}

取消請求

可以利用XMLHttpRequest實例對象上的方法abort()取消還沒有獲得響應的請求。

xhr.abort();

重復請求問題

有一種場景:用戶點擊按鈕,發送請求。但是如果用戶不斷點擊按鈕就可能發生不斷發請求的情況,服務器就需要處理相同的請求,從而造成服務器的壓力比較大。
例如:


在這里插入圖片描述

以上便是用戶點擊多次的一個情況,可以看到請求也發了多次。

為了避免這個情況,需要在用戶多次點擊的時候進行處理,比如:當用戶點擊多次的時候,如果上一次請求還在發送過程中,則取消上一次的請求,重新開啟一個新的請求。這種方式也就是我們經常所說的防抖解決方案。

let xhr = null;
    let isSending = false; // 標識是否正在發送請求
    btn.addEventListener('click', function () {
        // 獲取xmlhttprequest實例
        if(isSending) xhr.abort();
        xhr = new XMLHttpRequest();
        isSending = true;
        xhr.responseType = 'json';

        xhr.onerror = function() {
            alert('哦豁,網絡出錯')
        }
        // 初始化
        xhr.open('POST','http://127.0.0.1:8000/server');
        xhr.setRequestHeader('uname', 'admin');
        // 發送請求
        xhr.send();
        // 事件綁定,處理響應結果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                isSending = false;
                if (xhr.status >= 200 && xhr.status < 300) {
                    // input.innerHTML = JSON.parse(xhr.responseText).uname;
                    input.innerHTML = xhr.response.uname;
                }
            }
        }
    })

其實除了防抖利用標識符解決外,最有效的方式則是禁用當前的按鈕,不允許用戶進行點擊,一方面告訴用戶請求已經發送了,另一方面也防止用戶多次點擊的情況。

Jquery中使用Ajax

  1. 引入jquery

這里采用cdn引入

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  1. 編寫get/post請求
$.get('http://127.0.0.1:8000/server',{a: 12, b: 34}, function (data) {
    console.log(data)
},'json')

$.post('http://127.0.0.1:8000/server', {a:12, b:34},function (data) {
    console.log(data)
}, 'json')

這兩個方法第一個參數表示請求的url,第二個參數表示傳遞的數據,第三個參數表示對返回的響應體的格式要求,也即responseType的值。

通過的請求方法:

$.ajax({
    // url
    url: 'http://127.0.0.1:8000/server',
        // method
        type: 'POST',
        // data
        data: {a: 1, b: 2},
    // 響應體類型
    dataType: 'json',
        // headers
        headers: {
            a: 1
    },
    // 超時時間設置
    timeout: 2000,
        // 成功回調
        success: function (data) {
            console.log(data);
    },
    // 失敗回調,包括網絡異常/超時/等情況
    error: function (err) {
        console.log(err);
    }
})

fetch發送請求

fetch屬于js全局的方法,所以可以直接調用,他是一個基于promise的http請求方法。且是一個異步請求。

// 當調用fetch的時候,基本上請求就開始發送了。
let f = fetch('http://127.0.0.1:8000/server', {
// 初始化配置選項
    method: 'POST',
        headers: {
            uname: 'chen'
    },
    // 請求體
    body: {
        uname: 'chen'
    }
});
f.then(response=> {
    // return response.text();// 返回一個已解決的響應體字符串表示的期約
    return response.json();// 返回一個已解決的響應體json表示的期約
}).then(data=> {
    console.log(data);
})

ajax工具庫-axios

axios基于promise的網絡請求庫,可以適用于瀏覽器和node環境

  1. 引入axios(采用CDN的方式)
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
  • 發送get請求
// 配置基礎的url
axios.defaults.baseURL = 'http://127.0.0.1:8000';

axios.get('/server', {
    params: {
        a: 1,
            b: 2
    },
    headers: {
        uname: 'chen'
    }
}).then(data=>{
    console.log(data);
})

第一個參數表示請求的路徑,如果預先設置了基礎的url,此處設置的url會跟基礎的url進行拼接。第二個參數是對請求的基礎配置

  • 發送post請求
// 配置基礎的url
axios.defaults.baseURL = 'http://127.0.0.1:8000
axios.post('/server', {
    // 請求體
    name: 'admin',
        password: '123'
}, {
    // 請求配置
    headers: {
        uname: 'chen'
    },
    // post請求也可以在Url后面添加額外的信息
    params: {
        a: 1,
            b: 2
    }
}).then(data=>{
    console.log(data);
})

第一個參數跟get請求一致,第二個參數表示請求體數據,第三個參數表示請求配置。需要注意的是,由于axios的post請求默認的Content-Type為application/json;charset=UTF-8,因此會觸發預檢請求,需要對數據進行配置/服務器進行配置

跨域問題

ajax請求是不支持跨域的,也就是ajax請求滿足同源策略。所以對于想要獲取不同源的數據,就需要解決跨域問題。

跨域問題的解決方案有:

  • JSONP

JSONP是一個非官方的跨域解決方案,只支持get請求。在網頁中一些標簽:img link iframe script。不受同源策略的影響。而JSONP就是利用script標簽的跨域能力來發送請求的。

script標簽如何發送請求呢?

我們知道如果要引入一個外部腳本,可以使用script標簽的src屬性進行指定。當瀏覽器解析到當前的這個script標簽的時候,會將其src的js腳本進行引入,類似于將js腳本中的代碼放在script標簽中相應的位置。

比如:當前有一個外部腳本test.js

console.log(124);

在html文件中進行引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSONP</title>
</head>
<body>
    <div></div>
    <script src="/js/test.js"></script>
</body>
</html>

此時瀏覽器解析完成時(這里先不考慮腳本中代碼的執行),HTML呈現為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSONP</title>
</head>
<body>
    <div></div>
    <script>
        console.log(124);
    </script>
</body>
</html>

可以看到對應的位置被外部Js腳本替換。(這里的外部js腳本必須是可執行的js腳本,如果js腳本里面是123,則認為是不合法的)

以上引入的js腳本是同一個域下的,同樣可以引入不同域下面的js腳本,我們用的比較多的就是cdn引入的jquery腳本

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

所以按照以上的思路,我們可以利用script標簽的src引入不同域的外部資源。但是如何獲取數據呢?
之前有提到過,script標簽引入的外部資源必須是可執行的js腳本。而如果簡單的想要獲取某個非可執行的js腳本數據,這是不合法的。
所以JSONP就說,既然需要執行的js腳本,那么服務器就給你提供一個客戶端已有的函數字符串。同時將你需要的數據拼接到函數字符串中。

描述比較生硬,可能不容易理解,可以看以下的例子。

具體實現:

客戶端:

<div class="btn">發送請求</div>
<div class="input"></div>
<script>
    let btn = document.querySelector('.btn');
    let input = document.querySelector('.input');
    
    function handle(data) {
        input.innerHTML = data.uname;
    }

    btn.addEventListener('click', function () {
        let script = document.createElement('script');
        // 發起請求
        script.src = 'http://127.0.0.1:8000/server';
        document.body.appendChild(script);
    })
</script>

服務器端(node):

app.get('/server',(req,res) => {
    let data = {
        uname: 'op'
    }

    let resData = JSON.stringify(data);

    // 將數據拼接到函數字符串中
    res.send(`handle(${resData})`);
})

可以看到服務器將含有數據的函數調用字符串返回給了客戶端,
具體返回的內容如下:
handle({"uname":"op"})

客戶端接收到了這個字符串內容,就會嘗試解析,類似于執行js腳本,由于客戶端之前有這個handle函數,所以就會直接調用handle函數,利用handle函數就可以獲取到服務器端傳過來的數據了。

利:

  • 實現簡單

弊:

  • 需要服務器知道客戶端已有的函數名稱
  • 容易遭受xss攻擊

  • jQuery發送jsonp

jquery有一個方法getJSON可以很方便得發送jsonp,同時不需要服務器端知道函數名稱得情況下傳遞函數字符串。

服務器(node):

app.get('/server',(req,res) => {
    let data = {
        uname: 'op'
    }

    let resData = JSON.stringify(data);
    // 獲取客戶端傳遞得函數名稱
    let ca = req.query.callback;
    res.send(`${ca}(${resData})`);
})

客戶端:

<div class="btn">發送請求</div>
<div class="input"></div>
<script>
    $('.btn').click(function () {
        $.getJSON('http://127.0.0.1:8000/server?callback=?',function (data) {
            $('.input').html(`${data.uname}`);
        })
    })
</script>

第一個參數為目標Url,這里需要注意得是后面必須拼接callback參數,否則服務端無法獲得函數名稱,第二個參數是函數回調,利用該函數可以獲取到服務器返回得數據。

  • CORS

CORS:跨域資源共享。是官方得跨域解決方案,特點:不需要客戶端做任何特殊得操作,完全在服務器中進行處理,支持get/post請求。
工作形式:當瀏覽器發現當前請求違反了CORS,則會發起一個預檢請求,詢問服務器是否允許這個請求,服務器通過設置響應頭告訴瀏覽器,允許該請求,則瀏覽器會將用戶發送得請求發送到指定得服務器。

一般設置以下響應頭信息:

  • Access-Control-Allow-Origin: 表示允許的訪問來源。或者可以使用"*" 表示允許所有的訪問來源。這個字段一般用于對跨域請求的支持。
  • Access-Control-Allow-Headers:表示允許的自定義請求頭。
  • Access-Control-Allow-Methods:表示允許的請求方式。

例子node():

app.use('/server', (req,res,next) => {
    // 設置所有域都可以訪問該域的資源
    res.setHeader('Access-Control-Allow-Origin','*');
    // 設置允許的自定義請求頭
    res.setHeader('Access-Control-Allow-Headers', '*');
    next();
})
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容