【JavaScript】從入門到深刻了解AJAX

寫在前面

AJAX做爲前端中的核心部分,咱們可能在工做中,只是搬瓦工的角色,因此只會在寫業務的時候給後端發送一個axiosfetch等請求,拿到數據渲染到頁面後就無論不顧了,可是做爲一個不想只是搬磚的工程師,深刻的瞭解它仍是很是有必要的,這篇文章將帶你深刻了解AJAXjavascript

關於ajax的基礎知識

ajax: 即async javascript and xml,就是異步的js和xmlhtml

關於XML

最先的時候,基於ajax從服務器獲取的數據通常都是xml格式數據,只不過如今基本都是應用更小巧更方便操做的json格式處理前端

  • html超文本標記語言
  • xhtml嚴謹的html
  • xml可拓展的標記語言(基於標籤結構存儲數據)

異步js

基於ajax實現局部刷新java

  • 服務器渲染 通常都是同步 全局刷新ios

  • 客戶端渲染 通常是異步 局部刷新git

基礎操做

對於ajax的操做有核心的四步操做:github

建立xhr對象

let xhr = new XMLHttpRequest;
// 不兼容XMLHttpRequest的瀏覽器用ActiveXObject
let xhr = new ActiveXObject
複製代碼

打開請求鏈接(配置請求信息)

xhr.open(method, url, async, user-name, user-pass),其中methodhttp請求的方法 不論是哪種請求方式,客戶端均可以把信息傳遞給服務器,服務器也能夠把信息返回給客戶端,只不過get偏向於拿(給的少拿得多)web

  • get:從服務器獲取ajax

    get請求url有長度限制:具體表現爲ie: 2083字符,大約爲2kb,谷歌: 8182字符大約爲4kbjson

    • get/head(只獲取響應頭信息,不獲取響應主題內容)

    • delete刪除,通常代指刪除服務器上指定的文件

    • options 試探性請求

      在cross跨域請求中,因此正常請求發送前,先發送一個試探請求,驗證是否能夠和服務器正常的創建鏈接

  • post:向服務器發送

    post偏向於給(給的多拿的少),post請求理論上沒有大小限制

    • post/put新增,通常代指向服務器中新增文件
  • get請求和post的區別

    • get請求相對於post來講,不安全,get請求傳參是基於URL問號傳參,會被別人基於URL劫持的方式把信息獲取到有一句話說得好:「互聯網面前人人都在裸奔」,因此沒有絕對的安全,咱們須要更多的去處理安全性。

    • get請求容易產生緩存,緣由仍是由於get是基於問號傳參傳遞信息的。瀏覽器在每一次獲取數據後,通常會緩存一下數據,下一次若是請求的地址和參數和上一次同樣,瀏覽器會直接獲取緩存中的數據,因此咱們基於get發送請求,須要清除緩存的時候,通常都會在地址欄中添加一個隨機數,相似於這樣:_ = Math.random

  • 基於get向服務器發送請求,傳遞給服務器的方式:

    • 基於請求頭傳遞給服務器(本地cookie信息傳遞給服務器)
    • 請求url地址後面的問號傳參 === 主要方式
    • 監聽請求狀態,在不一樣狀態中作不一樣的事情
    • 發送AJAX請求 ajax任務開始 直到響應主體信息返回
  • 基於post向服務器發送請求,傳遞給服務器的方式

傳遞給服務器的數據格式爲application/x-www--form-urlencoded xx=xxx&xx=xxxxmultipart/form-data(表單提交、文件上傳)、raw(能夠上傳textjsonxml等格式的文本)、binary(上傳二進制數據或者編碼格式的數據)

let xhr = new XMLHttpRequest
複製代碼
  • ajax狀態碼
0 1 2 3 4
UNSENT OPENED HEADERS_RECEIVED LOADING DONE
建立完 完成open操做 響應頭信息回來 響應主體信息正在返回中 響應主體已經返回
  • HTTP狀態碼

這裏只列舉常見狀態碼

200: OK,請求成功。通常用於GET與POST請求

304: Not Modified,未修改。所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。客戶端一般會緩存訪問過的資源,經過提供一個頭信息指出客戶端但願只返回在指定日期以後修改的資源

400: Bad Request,客戶端請求的語法錯誤,服務器沒法理解

401: Unauthorized,請求要求用戶的身份認證

403: Forbidden,服務器理解請求客戶端的請求,可是拒絕執行此請求

404: Not Found,服務器沒法根據客戶端的請求找到資源(網頁)。經過此代碼,網站設計人員可設置"您所請求的資源沒法找到"的個性頁面

500: Internal Server Error,服務器內部錯誤,沒法完成請求

502: Bad Gateway,做爲網關或者代理工做的服務器嘗試執行請求時,從遠程服務器接收到了一個無效的響應

503: Service Unavailable,因爲超載或系統維護,服務器暫時的沒法處理客戶端的請求。延時的長度可包含在服務器的Retry-After頭信息中

504: Gateway Time-out,充當網關或代理的服務器,未及時從遠端服務器獲取請求

注意:在真實項目中,後臺開發者可能不是按照這個規則來處理的,無論傳參或者權限是否正確等,只要服務器接收到請求最後都給返回200,在返回的json數據,基於某個字段表示錯誤信息。因此咱們要和後端溝通好~

  • xhr的一些方法
status = xhr.status;  // http狀態碼

state = xhr.readyState;

if(/^(2|3)\d{2}$/.test(status))  // 成功

xhr.getAllResponseHeaders()  響應頭信息

xhr.getAllResponse("date")  服務器日期是格林尼治時間GMT  比北京時間晚了八小時  new Date處理

xhr.response         // 響應主體信息

xhr.setheaders

xhr.timeout

xhr.withCredentials   // 容許攜帶跨域

xhr.abort()  打斷請求

// 請求頭信息不能是中文和特殊字符,要編碼
複製代碼

ajax幾種狀態的同異步問題

let xhr = new XMLHttpRequest;
 /**
  *  xhr.readyState
  *  UNSENT 0 建立完默認爲0
  *  OPENED 1 已經完成open操做
  *  HEADERS_RECEIVED 2 服務器已經把響應頭信息返回了
  *  LOADING 3 響應主體正在返回中
  *  DONE 4 響應主體已經返回、
  *  xhr.open第三個參數控制的同步異步指的是從當前send發送請求開始,算任務開始,一直到狀態爲4纔算任務結束,同步:在此期間 全部的事件都不去處理,
  *  而異步是:在此期間,該幹啥幹啥。
  *  異步 在SEND後會把這個請求的任務放在EVENT QUEUE中,屬於宏任務
  */
// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", true);  // true爲異步 false爲同步
// // console.log(xhr.readyState);   // 1
// xhr.onreadystatechange = function() {
//     // 堅挺到狀態改變後纔會觸發的事件
//     console.log(xhr.readyState);    // 2,3,4
    
// } 
// xhr.send();   // 任務開始

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", true);  // true爲異步 false爲同步
// xhr.send();   // 任務開始
 // 此時狀態是1
// xhr.onreadystatechange = function() {
//     // 堅挺到狀態改變後纔會觸發的事件
//     console.log(xhr.readyState);    // 2,3,4
    
// } 

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", false);  // true爲異步 false爲同步   同步特色 任務狀態 不爲4啥也幹不了
// xhr.send();   // 任務開始

// // 狀態爲4,當下面變爲5時纔會改變, 因此沒輸出

// xhr.onreadystatechange = function() {   
//     console.log(xhr.readyState);      // 沒有輸出
// } 

// let xhr = new XMLHttpRequest;
// xhr.open("get", "./2.js", false);  // true爲異步 false爲同步   同步特色 任務狀態 不爲4啥也幹不了

// // 這裏狀態爲1, 
// xhr.onreadystatechange = function() {     // 這也是異步 的
//     console.log(xhr.readyState);      // 4
// } 

// xhr.send();   // 同步任務開始 狀態不爲4 啥也幹不了  任務爲4的時候,主棧才結束

let xhr = new XMLHttpRequest;
xhr.open("get", "./2.js", false);  // true爲異步 false爲同步   同步特色 任務狀態 不爲4啥也幹不了

// 這裏狀態爲1, 
xhr.onreadystatechange = function() {     // 這也是異步 的
    console.log(xhr.readyState);      // 4
} 

xhr.send();   // 同步任務開始 狀態不爲4 啥也幹不了  任務爲4的時候,主棧才結束
複製代碼

實現倒計時功能

其實倒計時功能對於前端開發而言,是很簡單的事情,可是在這簡單的事情中,咱們還要注意些什麼呢?

假如咱們如今想要實現一個倒計時搶購功能,咱們可能第一下想到的就是獲取本地時間,計算與目標時間的時間差,試想一下,這樣作會存在哪些問題?

你們都據說過這樣一句話:「不要相信前端傳送的任何數據」。是這樣的,前端全部的數據都是可修改的,那麼倒計時也是同理,若是我修改了系統時間豈不是就能夠提早搶購了🐶

因此爲了不這種狀況的發生,咱們須要在真實項目中獲取服務器的時間~

對於這件事情,我問過相關同事的處理方案,他們團隊是經過接口獲取的開始時間與結束時間,當開始時才顯示,不開始時不顯示任何倒計時,固然這個是和需求有關係的,若是想在開始前顯示倒計時,那麼獲取服務器當前時間是必不可少的~

那麼獲取服務器的時間,又會存在什麼樣的問題呢?

咱們要基於ajax請求獲取響應頭中的信息,那麼在客戶端和服務端通訊的過程當中,勢必存在時間的消耗,那麼到達本地的時間就會存在必定的偏差,這個偏差是根據網絡環境等因素影響的,差值也並非固定的,因此它是必定存在的,咱們在開發過程當中,也只能是儘量的去減小這個偏差~

假如咱們搶購的目標時間是2020.6.10 20:00【這個模塊是在6月10號這一天寫的,哈哈哈】,下面咱們完成這個倒計時搶購功能

  • 首先在頁面中要有一個顯示倒計時時間的區域
<div>
    距離搶購時間還剩:
    <span id="spanBox"></span>
</div>
複製代碼
  • 封裝獲取服務器時間函數
function queryServerTime() {
    return new Promise(resolve => {
        let xhr = new XMLHttpRequest;
        xhr.open("head", './data.json');
        xhr.onreadystatechange = function() {
            if(!/^(2|3)\d{2}$/.test(xhr.status)) return;
            if(xhr.readyState === 2) {  // 響應頭信息返回時,即獲取
                // 響應頭信息已經返回
                let time = xhr.getResponseHeader("date");
                time = new Date(time);
                resolve(time)
            }
        }
        xhr.send();
    })
}
複製代碼
  • 業務函數
async function init() {
    let serverTime = await queryServerTime(),
        targetTime = new Date('2020/06/10 20:00:00'),
        autoTimer = null;

    // 計算時間差
    function computed() {
        let spanTime = targetTime - serverTime;
        if(spanTime <= 0) {
            spanBox.innerHTML = `00:00:00`;
            clearInterval(autoTimer);
        }
        let hours = Math.floor(spanTime/(60*60*1000));
        spanTime = spanTime - hours*60*60*1000;
        let minutes = Math.floor(spanTime/(60*1000))
        spanTime = spanTime -minutes*60*1000;
        let seconds = Math.floor(spanTime / 1000)

        hours = hours < 10 ? "0" + hours : hours;
        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;
        spanBox.innerHTML = `${hours}:${minutes}:${seconds}`;
    }
    computed();

    // 1s計算一次
    autoTimer = setInterval(_ => {
        // 從新獲取服務器時間  可是這樣有很大延遲 、服務器壓力也大
        // 咱們能夠基於第一次獲取的時間,在原來的基礎上,讓其自動累加
        serverTime = new Date(serverTime.getTime() + 1000)
        computed()
    }, 1000)
}

init();
複製代碼

這樣,倒計時的功能就基於ajax實現了,完整的代碼在這裏

封裝一個本身的ajax

說到封裝,個人第一篇掘金博客就是封裝一個本身的ajax,不過那個有些過於「單薄」,連接在這裏:封裝ajax請求的兩種方式

對於封裝的過程本篇文章不詳細列舉~封裝好的、更完善一些的ajax在這裏,各位小夥伴移步這裏獲取哦~

最後

本篇文章從說ajax的基礎一路到封裝,若是文章有不對之處還請你們指出,咱們共同窗習~共同進步~

最後,推薦一下個人公衆號「web前端日記」,歡迎小夥伴前來關注哦~

相關文章
相關標籤/搜索