您好,登錄后才能下訂單哦!
前言
axios 是 Vue 官方推薦的一個 HTTP 庫,用 axios 官方簡介來介紹它,就是:
Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。
作為一個優秀的 HTTP 庫,axios 打敗了曾經由 Vue 官方團隊維護的 vue-resource,獲得了 Vue 作者尤小右的大力推薦,成為了 Vue 項目中 HTTP 庫的最佳選擇。
雖然,axios 是個優秀的 HTTP 庫,但是,直接在項目中使用并不是那么方便,所以,我們需要對其進行一定程度上的配置封裝,減少重復代碼,方便調用。下面,我們就來聊聊 Vue 中 axios 的封裝。
開始
其實,網上關于 axios 封裝的代碼不少,但是大部分都是在入口文件(main.js)中進行 axios 全局對象屬性定義的形式進行配置,類似于如下代碼:
axios.defaults.timeout = 10000
該方案有兩個不足,首先,axios 封裝代碼耦合進入入口文件,不方便后期維護;其次,使用 axios 全局對象屬性定義的方式進行配置,代碼過于零散。
針對問題一,我使用了 Vue 源碼結構中的一大核心思想——將功能拆分為文件,方便后期的維護。單獨創建一個 http.js 或者 http.ts 文件,在文件中引入 axios 并對其進行封裝配置,最后將其導出并掛載到 Vue 的原型上即可。此時,每次修改 axios 配置,只需要修改對應的文件即可,不會影響到不相關的功能。
針對問題二,采用 axios 官方推薦的,通過配置項創建 axios 實例的方式進行配置封裝。
代碼如下:
// http.js import axios from 'axios' // 創建 axios 實例 const service = axios.create({ // 配置項 })
根據環境設置 baseURL
baseURL 屬性是請求地址前綴,將自動加在 url 前面,除非 url 是個絕對地址。正常情況下,在開發環境下和生產模式下有著不同的 baseURL,所以,我們需要根據不同的環境切換不同的 baseURL。
在開發模式下,由于有著 devServer 的存在,需要根據固定的 url 前綴進行請求地址重寫,所以,在開發環境下,將 baseURL 設為某個固定的值,比如:/apis。
在生產模式下,根據 Java 模塊的請求前綴的不同,可以設置不同的 baseURL。
具體代碼如下:
// 根據 process.env.NODE_ENV 區分狀態,切換不同的 baseURL const service = axios.create({ baseURL: process.env.NODE_ENV === 'production' ? `/java` : '/apis', })
統一設置請求頭
在這里和大家聊一個問題,什么是封裝?在我看來,封裝是通過更少的調用代碼覆蓋更多的調用場景。
由于,大部分情況下,請求頭都是固定的,只有少部分情況下,會需要一些特殊的請求頭,所以,在這里,我采用的方案是,將普適性的請求頭作為基礎配置。當需要特殊請求頭時,將特殊請求頭作為參數傳入,覆蓋基礎配置。
代碼如下:
const service = axios.create({ ... headers: { get: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' // 在開發中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進來 }, post: { 'Content-Type': 'application/json;charset=utf-8' // 在開發中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進來 } }, })
跨域、超時、響應碼處理
axios 中,提供是否允許跨域的屬性——withCredentials,以及配置超時時間的屬性——timeout,通過這兩個屬性,可以輕松處理跨域和超時的問題。
下面,我們來說說響應碼處理:
axios 提供了 validateStatus 屬性,用于定義對于給定的HTTP 響應狀態碼是 resolve 或 reject promise。所以,正常設置的情況下,我們會將狀態碼為 2 系列或者 304 的請求設為 resolve 狀態,其余為 reject 狀態。結果就是,我們可以在業務代碼里,使用 catch 統一捕獲響應錯誤的請求,從而進行統一處理。
但是,由于我在代碼里面使用了 async-await,而眾所周知,async-await 捕獲 catch 的方式極為麻煩,所以,在此處,我選擇將所有響應都設為 resolve 狀態,統一在 then 處理。
此部分代碼如下:
const service = axios.create({ // 跨域請求時是否需要使用憑證 withCredentials: true, // 請求 30s 超時 timeout: 30000, validateStatus: function () { // 使用async-await,處理reject情況較為繁瑣,所以全部返回resolve,在業務代碼中處理異常 return true }, })
請求、響應處理
在不使用 axios 的情況下,每次請求或者接受響應,都需要將請求或者響應序列化。
而在 axios 中, transformRequest 允許在向服務器發送請求前,修改請求數據;transformResponse 在傳遞給 then/catch 前,允許修改響應數據。
通過這兩個鉤子,可以省去大量重復的序列化代碼。
代碼如下:
const service = axios.create({ // 在向服務器發送請求前,序列化請求數據 transformRequest: [function (data) { data = JSON.stringify(data) return data }], // 在傳遞給 then/catch 前,修改響應數據 transformResponse: [function (data) { if (typeof data === 'string' && data.startsWith('{')) { data = JSON.parse(data) } return data }] })
攔截器
攔截器,分為請求攔截器以及響應攔截器,分別在請求或響應被 then 或 catch 處理前攔截它們。
之前提到過,由于 async-await 中 catch 難以處理的問題,所以將出錯的情況也作為 resolve 狀態進行處理。但這帶來了一個問題,請求或響應出錯的情況下,結果沒有數據協議中定義的 msg 字段(消息)。所以,我們需要在出錯的時候,手動生成一個符合返回格式的返回數據。
由于,在業務中,沒有需要在請求攔截器中做額外處理的需求,所以,請求攔截器的 resolve 狀態,只需直接返回就可以了。
請求攔截器代碼如下:
// 請求攔截器 service.interceptors.request.use((config) => { return config }, (error) => { // 錯誤拋到業務代碼 error.data = {} error.data.msg = '服務器異常,請聯系管理員!' return Promise.resolve(error) })
再來聊聊響應攔截器,還是之前的那個問題,除了請求或響應錯誤,還有一種情況也會導致返回的消息體不符合協議規范,那就是狀態碼不為 2 系列或 304 時。此時,我們還是需要做一樣的處理——手動生成一個符合返回格式的返回數據。但是,有一點不一樣,我們還需要根據不同的狀態碼生成不同的提示信息,以方便處理上線后的問題。
響應攔截器代碼如下:
// 根據不同的狀態碼,生成不同的提示信息 const showStatus = (status) => { let message = '' // 這一坨代碼可以使用策略模式進行優化 switch (status) { case 400: message = '請求錯誤(400)' break case 401: message = '未授權,請重新登錄(401)' break case 403: message = '拒絕訪問(403)' break case 404: message = '請求出錯(404)' break case 408: message = '請求超時(408)' break case 500: message = '服務器錯誤(500)' break case 501: message = '服務未實現(501)' break case 502: message = '網絡錯誤(502)' break case 503: message = '服務不可用(503)' break case 504: message = '網絡超時(504)' break case 505: message = 'HTTP版本不受支持(505)' break default: message = `連接出錯(${status})!` } return `${message},請檢查網絡或聯系管理員!` } // 響應攔截器 service.interceptors.response.use((response) => { const status = response.status let msg = '' if (status < 200 || status >= 300) { // 處理http錯誤,拋到業務代碼 msg = showStatus(status) if (typeof response.data === 'string') { response.data = { msg } } else { response.data.msg = msg } } return response }, (error) => { // 錯誤拋到業務代碼 error.data = {} error.data.msg = '請求超時或服務器異常,請檢查網絡或聯系管理員!' return Promise.resolve(error) })
tips:友情提示,上面那一坨 switch-case 代碼,可以使用策略模式進行優化~
支持 TypeScript
由于前段時間,我在部門內推了 TypeScript,為了滿足自己的強迫癥,將所有 js 文件改寫為了 ts 文件。由于 axios 本身有 TypeScript 相關的支持,所以只需要把對應的類型導入,然后賦值即可。
完整代碼
// http.ts import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' const showStatus = (status: number) => { let message = '' switch (status) { case 400: message = '請求錯誤(400)' break case 401: message = '未授權,請重新登錄(401)' break case 403: message = '拒絕訪問(403)' break case 404: message = '請求出錯(404)' break case 408: message = '請求超時(408)' break case 500: message = '服務器錯誤(500)' break case 501: message = '服務未實現(501)' break case 502: message = '網絡錯誤(502)' break case 503: message = '服務不可用(503)' break case 504: message = '網絡超時(504)' break case 505: message = 'HTTP版本不受支持(505)' break default: message = `連接出錯(${status})!` } return `${message},請檢查網絡或聯系管理員!` } const service = axios.create({ // 聯調 baseURL: process.env.NODE_ENV === 'production' ? `/` : '/apis', headers: { get: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' }, post: { 'Content-Type': 'application/json;charset=utf-8' } }, // 是否跨站點訪問控制請求 withCredentials: true, timeout: 30000, transformRequest: [(data) => { data = JSON.stringify(data) return data }], validateStatus () { // 使用async-await,處理reject情況較為繁瑣,所以全部返回resolve,在業務代碼中處理異常 return true }, transformResponse: [(data) => { if (typeof data === 'string' && data.startsWith('{')) { data = JSON.parse(data) } return data }] }) // 請求攔截器 service.interceptors.request.use((config: AxiosRequestConfig) => { return config }, (error) => { // 錯誤拋到業務代碼 error.data = {} error.data.msg = '服務器異常,請聯系管理員!' return Promise.resolve(error) }) // 響應攔截器 service.interceptors.response.use((response: AxiosResponse) => { const status = response.status let msg = '' if (status < 200 || status >= 300) { // 處理http錯誤,拋到業務代碼 msg = showStatus(status) if (typeof response.data === 'string') { response.data = {msg} } else { response.data.msg = msg } } return response }, (error) => { // 錯誤拋到業務代碼 error.data = {} error.data.msg = '請求超時或服務器異常,請檢查網絡或聯系管理員!' return Promise.resolve(error) }) export default service
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。