首頁 > 軟體

一文掌握ajax、fetch和axios的區別對比

2022-06-01 18:02:02

AJAX、Fetch、axios

AJAX

AJAX可以在不更新全域性的情況下更新區域性頁面。通過在與伺服器進行資料交換,可以使網頁實現非同步更新。

AJAX的原理就是通過XHR物件來向伺服器發起非同步請求,從伺服器獲得資料,然後用JS來操作DOM更新頁面。領導想找小李彙報一下工作,就委託祕書去叫小李,自己就接著做其他事情,直到祕書告訴他小李已經到了,最後小李跟領導彙報工作。Ajax請求資料流程與“領導想找小李彙報一下工作”類似,上述祕書就相當於XMLHttpRequest物件,領導相當於瀏覽器,響應資料相當於小李。瀏覽器可以傳送HTTP請求後,接著做其他事情,等收到XHR返回來的資料再進行操作。

建立AJAX

// 1. 建立 XMLHttpRequest 範例
let xhr = XMLHttpRequest()
// 2. 開啟和伺服器的連線
xhr.open('get', 'URL')
// 3.傳送
xhr.send()
// 4. 接收變化。
xhr.onreadystatechange = () => {
    if(xhr.readyState == 4 && xhr.status == 200){   // readyState: ajax 狀態,status:http 請求狀態
        console.log(xhr.responseText);   //響應主體
    }
}
  • 建立AJAX範例:let xhr = new XMLHttpRequest()
  • 開啟請求,設定請求前的設定項:xhr.open([http method], [url], [async], [userName], [userPass])
  1. http methods 請求方式:post,get,delete,put,head,options,trace,connect
  2. url:想伺服器請求的路徑
  3. async:是否為非同步請求
  4. userName、userPass:使用者名稱與密碼
  • 通過XMLHttpRequest.open()方法與伺服器建立連線
  • 傳送請求:XMLHttpRequest.send() 方法中如果 Ajax 請求是非同步的則這個方法傳送請求後就會返回,如果Ajax請求是同步的,那麼請求必須知道響應後才會返回。
  • 通過XMLHttpRequest物件的onreadystatechange事件監聽伺服器端的通訊狀態
  • 接收資料並進行處理
  • 將處理後的結果更新到頁面上

AJAX的缺點:

  • 本是針對MVC架構,不符合前端MVVM的浪潮
  • 基於原生的XHR開發
  • 設定和呼叫方式混亂

axios原理

axios是使用promise封裝的ajax,它內部有兩個攔截器,分別是request攔截器和response攔截器。

  • 請求攔截器的作用是在請求傳送之前進行一些操作,例如在每個請求體上加入token
  • 響應攔截器的作用是接收到響應後做的一些操作,例如登入失效後需要重新登入跳轉到登入頁

axios的特點

  • 由瀏覽器端發起請求,在瀏覽器中建立XHR
  • 支援promise API
  • 監聽請求和返回
  • 更好的格式化,自動將資料轉換為json資料
  • 安全性更高,可抵禦CSRF攻擊

axios常用的方法

axios常用的方法有getpostputpatchdelete等。其中getpost返回的都是promise物件,可以使用promise方法

axios.get(url[, config]):get請求用於列表和資訊查詢

axios.get('apiURL', {
    param: {
        id: 1
    }
    // param 中的的鍵值對最終會 ? 的形式,拼接到請求的連結上,傳送到伺服器。
}).then(res => {
    console.log(res);
})
.catch( error => {
    console.log(error)
}

axios.delete(url[, config]):刪除

axios.delete('apiURL', {
    params: {
        id: 1
    },
    timeout: 1000
})

axios.post(url[, data[, config]]):post請求用於資訊的新增

axios.post('apiURL',{
        user: '小新',
        age: 18
}).then( res => {
    console.log(res);
})
.catch( error => {
    console.log(error)
}

axios.put(url[, data[, config]]):更新操作

axios.put('apiURL', {
    name: '小新',
})

axios.patch(url[, data[, config]]):更新操作

axios.patch('apiURL', {
    id: 13,
},{
   timeout: 1000,
})

put和patch的區別

patch方法用來更新區域性資源,假設我們有一個UserInfo,裡面有userId,userName,userGender等10個欄位。可你的編輯功能因為需求,在某個特別的頁面裡只能修改userName,這個時候就可以使用patch

put也適用於更新資料,但必須提供完整的資源物件。

axios相關設定

  • url:用於請求伺服器的url
  • method:請求方法,預設為get
  • baseURL:會自動加到url前面
  • proxy:用於設定代理
  • transformRequest:允許在伺服器傳送請求之前修改請求資料

axios攔截器執行順序問題

  • 請求攔截:axios的請求攔截器會先執行最後指定的回撥函數,再依次向前執行
  • 響應攔截:axios的響應攔截器會先執行最先執行的回撥函數,再依次向前執行

例如:

axios.interceptors.request.use(config => {
  console.log(`請求攔截1`);
  return config;
});
axios.interceptors.request.use(config => {
  // 在傳送請求之前做些什麼 
  console.log(`請求攔截2`);
  return config;
});

// 新增響應攔截器 
axios.interceptors.response.use(response => {
  // 對響應資料做點什麼 
  console.log(`成功的響應攔截1`);
  return response.data;
});

// 新增響應攔截器 
axios.interceptors.response.use(response => {
  // 對響應資料做點什麼 
  console.log(`成功的響應攔截2`);
  return response;
});

// 傳送請求 
axios.get('/posts')
  .then(response => {
    console.log('成功了');
  }) 

執行結果為

console.log("請求攔截2");
console.log("請求攔截1");
console.log("成功的響應攔截1");
console.log("成功的響應攔截2");
console.log("成功了");

為什麼axios中需要攔截器

在SPA應用中,通常會使用token進行使用者身份認證,這就要求每次請求必須攜帶使用者的身份資訊,針對這個需求,為了避免在每個請求中單獨處理,我們可以通過封裝統一的request函數來為每隔請求統一新增token資訊。

但如果想為某些請求新增快取時間或者控制某些請求的呼叫頻率的話,我們就需要不斷地修改request函數來擴充套件對應的功能。此時,如果在考慮對響應進行統一處理,我們的request函數將變得越來越龐大,也越來越難維護。所以axios為我們提供了攔截器。

為什麼請求攔截2會在請求攔截1之前執行呢?

axios原始碼中將傳送請求分為了請求攔截器、傳送請求、響應攔截器、相應回撥,通過Promise的鏈式呼叫將這些部分結合起來了,這樣就得到了傳送請求拿到資料的全部過程。

下面分析原始碼:

  • 程式碼開始構建了一個config設定物件,用於第一次執行Promise返回一個成功的Promise
  • 最核心的陣列chain,這個陣列中儲存了請求攔截器、響應攔截器和傳送請求函數。該陣列中間放的是傳送請求的函數,左邊放的是請求攔截器,右邊放的是響應攔截器。在第一步中返回的Promise物件,將遍歷chain陣列逐一執行裡面的函數,並返回新的Promise物件
  • 往陣列中新增請求攔截函數,依照axios請求的執行順序,請求攔截器應該在傳送請求之前執行,故應該新增在傳送請求函數的前面,使用unshift方法
  • 往陣列中新增響應攔截器函數,依照axios請求的執行順序,響應攔截器應該在傳送請求之後執行,故應該新增在傳送請求函數的後面,所以使用的是陣列的push方法
  • Promise遍歷執行,每次從chain中取出兩個 函數執行(一個成功回撥,一個失敗回撥)
  • 最後返回一個Promise物件,用於執行響應資料的回撥

fetch
fetch是http請求資料的方式,它使用Promise,但不使用回撥函數。fetch採用模組化設計,通過資料流處理資料,對於請求大檔案或網速慢的情況相當有用。預設情況下fetch不會接收或傳送cookies。

優點:

  • 採用模組化思想,將輸入、輸出、狀態跟蹤分離
  • 基於promise,返回一個promise物件

缺點:

  • 過於底層,有很多狀態碼沒有進行封裝
  • 無法阻斷請求
  • 相容性差無法檢測請求進度

Fetch、ajax與axios的區別

  • 傳統的ajax利用的是HMLHttpRequest這個物件,和後端進行互動。
  • JQury ajax是對原生XHR的封裝,多請求間有巢狀的話就會出現回撥地獄的問題。
  • axios使用promise封裝XHR,解決了回撥地獄的問題。而Fetch沒有使用XHR,使用的是promise

Fetch和Ajax比有什麼優點

Fetch使用的是promise,方便使用非同步,沒有回撥地獄的問題。

總結

Ajax是一種web資料互動的方式,它可以使頁面在不重新載入的情況下請求資料並進行區域性更新,它內部使用了XHR來進行非同步請求。Ajax在使用XHR發起非同步請求時得到的是XML格式的資料,如果想要JSON格式,需要進行額外的轉換;Ajax本身針對的是MVC框架,不符合現在的MVVM架構Ajax有回撥地獄問題;Ajax的設定複雜

Fetch是XHR的代替品,它基於Promise實現的,並且不使用回撥函數,它採用模組化結構設計,並使用資料流進行傳輸,對於大檔案和網速慢的情況非常友好。但是Fetch不會對請求和響應進行監聽;不能阻斷請求;過於底層,對一些狀態碼沒有封裝;相容性差。

axios是基於PromiseXHR進行封裝,它內部封裝了兩個攔截器,分別是請求攔截器和響應攔截器。請求攔截器用於在請求發出之前進行一些操作,比如:設定請求體,攜帶Cookie、token等;響應攔截器用於在得到響應後進行一些操作,比如:登入失效後跳轉到登入頁面重新登入。axios有get、post、put、patch、delete等方法。axios可以對請求和響應進行監聽;返回Promise物件,可以使用Promise的API;返回JSON格式的資料;由瀏覽器發起請求;安全性更高,可以抵禦CSRF攻擊。

axios原始碼分析

axios的執行流程

  • 使用axios.create建立單獨的範例,或直接使用axios範例
  • 對於axios呼叫進入到request()中進行處理
  • 執行請求攔截器
  • 請求資料轉換器,將傳入的資料進行處理,比如JSON.stringify(data)
  • 執行介面卡,判斷是瀏覽器端還是node端,以執行不同的方法
  • 響應資料轉換器,對伺服器端的資料進行處理,比如JSON.parse(data)
  • 執行響應攔截器,對伺服器端資料進行處理,比如token失效跳轉到登入頁
  • 返回資料

入口檔案(lib/axios.js)

匯出的axios就是 範例化後的物件,還在其上掛載create方法,以供建立獨立的範例,實現範例之間互不影響。

// 建立範例過程的方法
function createInstance(defaultConfig) {
  return instance;
}
// 範例化
var axios = createInstance(defaults);
 
// 建立獨立的範例,隔離作用域
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// 匯出範例
module.exports = axios;

createInstance()

function createInstance(defaultConfig) {
  // 範例化,建立一個上下文
  var context = new Axios(defaultConfig);
 
  // 平時呼叫的 get/post 等等請求,底層都是呼叫 request 方法
  // 將 request 方法的 this 指向 context(上下文),形成新的範例
  var instance = bind(Axios.prototype.request, context);
 
  // Axios.prototype 上的方法 (get/post...)掛載到新的範例 instance 上,
  // 並且將原型方法中 this 指向 context
  utils.extend(instance, Axios.prototype, context);
 
  // Axios 屬性值掛載到新的範例 instance 上
  // 開發中才能使用 axios.default/interceptors
  utils.extend(instance, context);
 
  return instance;
}

createInstance執行流程:

  • 通過建構函式Axios建立範例context,作為下面request方法的上下文(this指向)
  • Axios.prototype.request方法作為範例使用,並把this指向context,形成新的範例instance
  • 將建構函式Axios.prototype上的方法掛載到新的範例instance上,然後將原型各個方法中的this指向context,這樣才能使用get、post等方法
  • Axios的屬性掛載到instance

可以看到axios不是簡單的建立範例context,而是在context上進行this繫結形成新的範例,然後將Axios屬性和請求方法掛載到新的範例上

攔截器(lib/core/InterceptorManager.js)

攔截器涉及一個屬性和三個方法:

  • handler:存放use註冊的回撥函數
  • use:註冊成功和失敗的回撥函數
  • eject:刪除註冊過的函數
  • forEach:遍歷回撥函數
function InterceptorManager() {
  // 存放 use 註冊的回撥函數
  this.handlers = [];
}

InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  // 註冊成功和失敗的回撥函數
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected,
    ...
  });
  return this.handlers.length - 1;
};

InterceptorManager.prototype.eject = function eject(id) {
  // 刪除註冊過的函數
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

InterceptorManager.prototype.forEach = function forEach(fn) {
  // 遍歷回撥函數,一般內部使用多
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

dispatchRequest(lib/core/dispatchRequest.js)

dispatchRequest主要做了以下操作:

  • transformRequest: 對 config 中的 data 進行加工,比如對 post 請求的 data 進行字串化(JSON.stringify(data))
  • adapter:介面卡,包含瀏覽器端 xhr 和 node 端的 http
  • transformResponse: 對伺服器端響應的資料進行加工,比如 JSON.parse(data)

取消請求(lib/cancel/CancelToken.js)

var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 處理錯誤
  }
});
// 取消請求(message 引數是可選的)
source.cancel('Operation canceled by the user.');
  • CancelToken 掛載 source 方法用於建立自身範例,並且返回 {token, cancel}
  • token 是建構函式 CancelToken 的範例,cancel 方法接收建構函式 CancelToken 內部的一個 cancel 函數,用於取消請求
  • 建立範例中,有一步是建立處於 pengding 狀態的 promise,並掛在實體方法上,外部通過引數 cancelToken 將範例傳遞進 axios 內部,內部呼叫 cancelToken.promise.then 等待狀態改變
  • 當外部呼叫方法 cancel 取消請求,pendding 狀態就變為 resolve,即取消請求並且丟擲 reject(message)

總結

  • 為了支援 axios() 簡潔寫法,內部使用 request 函數作為新範例
  • 使用 promsie 鏈式呼叫的巧妙方法,解決順序呼叫問題
  • 資料轉換器方法使用陣列存放,支援資料的多次傳輸與加工
  • 介面卡通過相容瀏覽器端和 node 端,對外提供統一 api
  • 取消請求這塊,通過外部保留 pendding 狀態,控制 promise 的執行時機

到此這篇關於一文掌握ajax、fetch和axios的區別對比的文章就介紹到這了,更多相關ajax、fetch和axios的比較內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com