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])
- 通過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常用的方法有get
、post
、put
、patch
、delete
等。其中get
和post
返回的都是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對象,用于執行響應數據的回調
fetchfetch
是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
是基于promise
對xhr
進行封裝,它內部封裝了兩個攔截器,分別是請求攔截器和響應攔截器。請求攔截器用于在請求發出之前進行一些操作,比如:設置請求體,攜帶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的比較內容請搜索碩編程以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持碩編程!