關(guān)于Vue中axios的封裝實例詳解

前言

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),赫章企業(yè)網(wǎng)站建設(shè),赫章品牌網(wǎng)站建設(shè),網(wǎng)站定制,赫章網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,赫章網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

axios 是 Vue 官方推薦的一個 HTTP 庫,用 axios 官方簡介來介紹它,就是:

Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。

作為一個優(yōu)秀的 HTTP 庫,axios 打敗了曾經(jīng)由 Vue 官方團(tuán)隊維護(hù)的 vue-resource,獲得了 Vue 作者尤小右的大力推薦,成為了 Vue 項目中 HTTP 庫的最佳選擇。

雖然,axios 是個優(yōu)秀的 HTTP 庫,但是,直接在項目中使用并不是那么方便,所以,我們需要對其進(jìn)行一定程度上的配置封裝,減少重復(fù)代碼,方便調(diào)用。下面,我們就來聊聊 Vue 中 axios 的封裝。

開始

其實,網(wǎng)上關(guān)于 axios 封裝的代碼不少,但是大部分都是在入口文件(main.js)中進(jìn)行 axios 全局對象屬性定義的形式進(jìn)行配置,類似于如下代碼:

axios.defaults.timeout = 10000

該方案有兩個不足,首先,axios 封裝代碼耦合進(jìn)入入口文件,不方便后期維護(hù);其次,使用 axios 全局對象屬性定義的方式進(jìn)行配置,代碼過于零散。

針對問題一,我使用了 Vue 源碼結(jié)構(gòu)中的一大核心思想——將功能拆分為文件,方便后期的維護(hù)。單獨創(chuàng)建一個 http.js 或者 http.ts 文件,在文件中引入 axios 并對其進(jìn)行封裝配置,最后將其導(dǎo)出并掛載到 Vue 的原型上即可。此時,每次修改 axios 配置,只需要修改對應(yīng)的文件即可,不會影響到不相關(guān)的功能。

針對問題二,采用 axios 官方推薦的,通過配置項創(chuàng)建 axios 實例的方式進(jìn)行配置封裝。

代碼如下:

// http.js
import axios from 'axios'
// 創(chuàng)建 axios 實例
const service = axios.create({
 // 配置項
})

根據(jù)環(huán)境設(shè)置 baseURL

baseURL 屬性是請求地址前綴,將自動加在 url 前面,除非 url 是個絕對地址。正常情況下,在開發(fā)環(huán)境下和生產(chǎn)模式下有著不同的 baseURL,所以,我們需要根據(jù)不同的環(huán)境切換不同的 baseURL。

在開發(fā)模式下,由于有著 devServer 的存在,需要根據(jù)固定的 url 前綴進(jìn)行請求地址重寫,所以,在開發(fā)環(huán)境下,將 baseURL 設(shè)為某個固定的值,比如:/apis。

在生產(chǎn)模式下,根據(jù) Java 模塊的請求前綴的不同,可以設(shè)置不同的 baseURL。

具體代碼如下:

// 根據(jù) process.env.NODE_ENV 區(qū)分狀態(tài),切換不同的 baseURL
const service = axios.create({
 baseURL: process.env.NODE_ENV === 'production' ? `/java` : '/apis',
})

統(tǒng)一設(shè)置請求頭

在這里和大家聊一個問題,什么是封裝?在我看來,封裝是通過更少的調(diào)用代碼覆蓋更多的調(diào)用場景。

由于,大部分情況下,請求頭都是固定的,只有少部分情況下,會需要一些特殊的請求頭,所以,在這里,我采用的方案是,將普適性的請求頭作為基礎(chǔ)配置。當(dāng)需要特殊請求頭時,將特殊請求頭作為參數(shù)傳入,覆蓋基礎(chǔ)配置。

代碼如下:

const service = axios.create({
 ...
 headers: {
 get: {
  'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  // 在開發(fā)中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進(jìn)來
 },
 post: {
  'Content-Type': 'application/json;charset=utf-8'
  // 在開發(fā)中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進(jìn)來
 }
 },
})

跨域、超時、響應(yīng)碼處理

axios 中,提供是否允許跨域的屬性——withCredentials,以及配置超時時間的屬性——timeout,通過這兩個屬性,可以輕松處理跨域和超時的問題。

下面,我們來說說響應(yīng)碼處理:

axios 提供了 validateStatus 屬性,用于定義對于給定的HTTP 響應(yīng)狀態(tài)碼是 resolve 或 reject promise。所以,正常設(shè)置的情況下,我們會將狀態(tài)碼為 2 系列或者 304 的請求設(shè)為 resolve 狀態(tài),其余為 reject 狀態(tài)。結(jié)果就是,我們可以在業(yè)務(wù)代碼里,使用 catch 統(tǒng)一捕獲響應(yīng)錯誤的請求,從而進(jìn)行統(tǒng)一處理。

但是,由于我在代碼里面使用了 async-await,而眾所周知,async-await 捕獲 catch 的方式極為麻煩,所以,在此處,我選擇將所有響應(yīng)都設(shè)為 resolve 狀態(tài),統(tǒng)一在 then 處理。

此部分代碼如下:

const service = axios.create({
 // 跨域請求時是否需要使用憑證
 withCredentials: true,
 // 請求 30s 超時
 timeout: 30000,
 validateStatus: function () {
  // 使用async-await,處理reject情況較為繁瑣,所以全部返回resolve,在業(yè)務(wù)代碼中處理異常
  return true
 },
})

請求、響應(yīng)處理

在不使用 axios 的情況下,每次請求或者接受響應(yīng),都需要將請求或者響應(yīng)序列化。

而在 axios 中, transformRequest 允許在向服務(wù)器發(fā)送請求前,修改請求數(shù)據(jù);transformResponse 在傳遞給 then/catch 前,允許修改響應(yīng)數(shù)據(jù)。

通過這兩個鉤子,可以省去大量重復(fù)的序列化代碼。

代碼如下:

const service = axios.create({
 // 在向服務(wù)器發(fā)送請求前,序列化請求數(shù)據(jù)
 transformRequest: [function (data) {
  data = JSON.stringify(data)
  return data
 }],
 // 在傳遞給 then/catch 前,修改響應(yīng)數(shù)據(jù)
 transformResponse: [function (data) {
  if (typeof data === 'string' && data.startsWith('{')) {
   data = JSON.parse(data)
  }
  return data
 }]
})

攔截器

攔截器,分為請求攔截器以及響應(yīng)攔截器,分別在請求或響應(yīng)被 then 或 catch 處理前攔截它們。

之前提到過,由于 async-await 中 catch 難以處理的問題,所以將出錯的情況也作為 resolve 狀態(tài)進(jìn)行處理。但這帶來了一個問題,請求或響應(yīng)出錯的情況下,結(jié)果沒有數(shù)據(jù)協(xié)議中定義的 msg 字段(消息)。所以,我們需要在出錯的時候,手動生成一個符合返回格式的返回數(shù)據(jù)。

由于,在業(yè)務(wù)中,沒有需要在請求攔截器中做額外處理的需求,所以,請求攔截器的 resolve 狀態(tài),只需直接返回就可以了。

請求攔截器代碼如下:

// 請求攔截器
service.interceptors.request.use((config) => {
 return config
}, (error) => {
 // 錯誤拋到業(yè)務(wù)代碼
 error.data = {}
 error.data.msg = '服務(wù)器異常,請聯(lián)系管理員!'
 return Promise.resolve(error)
})

再來聊聊響應(yīng)攔截器,還是之前的那個問題,除了請求或響應(yīng)錯誤,還有一種情況也會導(dǎo)致返回的消息體不符合協(xié)議規(guī)范,那就是狀態(tài)碼不為 2 系列或 304 時。此時,我們還是需要做一樣的處理——手動生成一個符合返回格式的返回數(shù)據(jù)。但是,有一點不一樣,我們還需要根據(jù)不同的狀態(tài)碼生成不同的提示信息,以方便處理上線后的問題。

響應(yīng)攔截器代碼如下:

// 根據(jù)不同的狀態(tài)碼,生成不同的提示信息
const showStatus = (status) => {
 let message = ''
 // 這一坨代碼可以使用策略模式進(jìn)行優(yōu)化
 switch (status) {
  case 400:
   message = '請求錯誤(400)'
   break
  case 401:
   message = '未授權(quán),請重新登錄(401)'
   break
  case 403:
   message = '拒絕訪問(403)'
   break
  case 404:
   message = '請求出錯(404)'
   break
  case 408:
   message = '請求超時(408)'
   break
  case 500:
   message = '服務(wù)器錯誤(500)'
   break
  case 501:
   message = '服務(wù)未實現(xiàn)(501)'
   break
  case 502:
   message = '網(wǎng)絡(luò)錯誤(502)'
   break
  case 503:
   message = '服務(wù)不可用(503)'
   break
  case 504:
   message = '網(wǎng)絡(luò)超時(504)'
   break
  case 505:
   message = 'HTTP版本不受支持(505)'
   break
  default:
   message = `連接出錯(${status})!`
 }
 return `${message},請檢查網(wǎng)絡(luò)或聯(lián)系管理員!`
}

// 響應(yīng)攔截器
service.interceptors.response.use((response) => {
 const status = response.status
 let msg = ''
 if (status < 200 || status >= 300) {
  // 處理http錯誤,拋到業(yè)務(wù)代碼
  msg = showStatus(status)
  if (typeof response.data === 'string') {
   response.data = { msg }
  } else {
   response.data.msg = msg
  }
 }
 return response
}, (error) => {
 // 錯誤拋到業(yè)務(wù)代碼
 error.data = {}
 error.data.msg = '請求超時或服務(wù)器異常,請檢查網(wǎng)絡(luò)或聯(lián)系管理員!'
 return Promise.resolve(error)
})

tips:友情提示,上面那一坨 switch-case 代碼,可以使用策略模式進(jìn)行優(yōu)化~

支持 TypeScript

由于前段時間,我在部門內(nèi)推了 TypeScript,為了滿足自己的強(qiáng)迫癥,將所有 js 文件改寫為了 ts 文件。由于 axios 本身有 TypeScript 相關(guān)的支持,所以只需要把對應(yīng)的類型導(dǎo)入,然后賦值即可。

完整代碼

// http.ts
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'

const showStatus = (status: number) => {
 let message = ''
 switch (status) {
 case 400:
  message = '請求錯誤(400)'
  break
 case 401:
  message = '未授權(quán),請重新登錄(401)'
  break
 case 403:
  message = '拒絕訪問(403)'
  break
 case 404:
  message = '請求出錯(404)'
  break
 case 408:
  message = '請求超時(408)'
  break
 case 500:
  message = '服務(wù)器錯誤(500)'
  break
 case 501:
  message = '服務(wù)未實現(xiàn)(501)'
  break
 case 502:
  message = '網(wǎng)絡(luò)錯誤(502)'
  break
 case 503:
  message = '服務(wù)不可用(503)'
  break
 case 504:
  message = '網(wǎng)絡(luò)超時(504)'
  break
 case 505:
  message = 'HTTP版本不受支持(505)'
  break
 default:
  message = `連接出錯(${status})!`
 }
 return `${message},請檢查網(wǎng)絡(luò)或聯(lián)系管理員!`
}

const service = axios.create({
 // 聯(lián)調(diào)
 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,在業(yè)務(wù)代碼中處理異常
 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) => {
 // 錯誤拋到業(yè)務(wù)代碼
 error.data = {}
 error.data.msg = '服務(wù)器異常,請聯(lián)系管理員!'
 return Promise.resolve(error)
})

// 響應(yīng)攔截器
service.interceptors.response.use((response: AxiosResponse) => {
 const status = response.status
 let msg = ''
 if (status < 200 || status >= 300) {
  // 處理http錯誤,拋到業(yè)務(wù)代碼
  msg = showStatus(status)
  if (typeof response.data === 'string') {
   response.data = {msg}
  } else {
   response.data.msg = msg
  }
 }
 return response
}, (error) => {
 // 錯誤拋到業(yè)務(wù)代碼
 error.data = {}
 error.data.msg = '請求超時或服務(wù)器異常,請檢查網(wǎng)絡(luò)或聯(lián)系管理員!'
 return Promise.resolve(error)
})

export default service

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對創(chuàng)新互聯(lián)的支持。

新聞標(biāo)題:關(guān)于Vue中axios的封裝實例詳解
本文路徑:http://muchs.cn/article28/jehgcp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、Google、網(wǎng)站維護(hù)品牌網(wǎng)站設(shè)計、微信小程序小程序開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)