前端廢物的自救之路(1)實(shí)現(xiàn)axios攔截器:對(duì)Nuxt.js中的@nuxtjs/axios進(jìn)行封裝
前言
手頭有一個(gè)去年五月寫的遺留項(xiàng)目辞色,以前使用的是Spring Boot
+Thymeleaf
+AmazeUI
(前臺(tái))+X-admin
(后臺(tái))蚊夫,由于用戶反映有許多需要改進(jìn)的地方(并且打開項(xiàng)目一看滥崩,那代碼我自己也看不懂了娇哆,而且以現(xiàn)在的視角看那些代碼真的是爛到極致),故進(jìn)行徹底重構(gòu)首懈。
由于前臺(tái)需要SEO华畏,并且考慮到上一版中使用AmazeUI
+Thymeleaf
開發(fā)的體驗(yàn)極為痛苦,且新的后端(Gin)的模板引擎十分惡心(可能有人不這么認(rèn)為跨嘉,反正我認(rèn)為Gin
的模板語法比Thymeleaf
惡心多了)川慌,于是前端選擇了Nuxt.js
。并且偿荷,由于前臺(tái)對(duì)響應(yīng)式有需求窘游,而且客戶認(rèn)為Vuetify
太花哨,iView
基礎(chǔ)版的組件又不夠用跳纳,所以UI框架前后臺(tái)統(tǒng)一使用Ant Design Vue
。
注:后端使用的是
Gin
贪嫂,并且實(shí)現(xiàn)了DDD
和CQS
寺庄。關(guān)于這個(gè)項(xiàng)目的后端我們過段時(shí)間再另行討論。但是為了解釋部分前端代碼力崇,這一系列文章中可能也會(huì)貼出一些后端代碼以供參考斗塘。其中前端代碼會(huì)給出具體的文件名和路徑,后端代碼會(huì)在代碼前標(biāo)識(shí)為“后端代碼”亮靴。
由于網(wǎng)絡(luò)上使用Nuxt.js
+Ant Design Vue
的文章實(shí)在是太少了馍盟,作為一個(gè)后端一天能寫600+行(以前用Java的時(shí)候一天最多甚至能寫1200+行),前端一天寫100行能工作的代碼就滿足了的前端廢物茧吊,為了讓以后再寫類似的項(xiàng)目的時(shí)候能不這么肉疼贞岭,于是決定記錄下本次開發(fā)的過程八毯,不為別的,就為自己以后再用Nuxt
和Ant Design Vue
的時(shí)候少掉幾根頭發(fā)瞄桨。
雖然這個(gè)項(xiàng)目不能開源话速,但是考慮到以后可能還會(huì)做類似的項(xiàng)目,并且類似的解決方案的Demo實(shí)在是太少了芯侥,所以在這個(gè)項(xiàng)目完結(jié)后我也會(huì)根據(jù)我的開發(fā)過程整理一個(gè)開源的種子項(xiàng)目發(fā)布在GitHub上以供參考和使用泊交。
為什么需要axios攔截器?
項(xiàng)目本身是前后端分離的柱查,鑒權(quán)使用的是JWT
廓俭。由于后端是根據(jù)請(qǐng)求頭中的X-Token
字段進(jìn)行鑒權(quán)的,所以需要在每個(gè)請(qǐng)求的請(qǐng)求中放入Token唉工。但是我們不可能在每個(gè)接口里都這么做白指,最佳的解決方案當(dāng)然是利用axios攔截器來完成這個(gè)工作。
另外酵紫,后端所有的響應(yīng)無論成功還是失敗都具有統(tǒng)一的格式:
后端代碼
// @Description 生成一個(gè)成功請(qǐng)求的標(biāo)準(zhǔn)響應(yīng)格式的JSON
// @param data interface{} 響應(yīng)中要攜帶的數(shù)據(jù)
// @return result 生成的JSON
func Success(data interface{}) (result gin.H) {
result = gin.H{
"code": Ok,
"message": "Success",
"data": data,
}
return
}
// @Description 生成一個(gè)失敗請(qǐng)求的標(biāo)準(zhǔn)響應(yīng)格式的JSON
// @param errorCode ErrorCode 錯(cuò)誤碼
// @param message string 要顯示給用戶的錯(cuò)誤信息
// @return result 生成的JSON
func Error(errorCode ErrorCode, message string) (result gin.H) {
result = gin.H{
"code": errorCode,
"message": message,
"data": nil,
}
return
}
我們也不可能在每個(gè)接口里都去解析這個(gè)格式告嘲,這樣不僅不優(yōu)雅還很容易出錯(cuò),這時(shí)候就需要axios攔截器對(duì)響應(yīng)做統(tǒng)一的處理了奖地。
封裝@nuxtjs/axios
由于Nuxt.js
對(duì)axios
做了封裝橄唬,所以使用起來與直接使用axios
有一些不同,在實(shí)現(xiàn)axios攔截器時(shí)與直接使用axios也不太一樣参歹。
注:我在新建項(xiàng)目的時(shí)候就選擇了axios仰楚,所以我的項(xiàng)目創(chuàng)建出來是自帶
@nuxtjs/axios
這個(gè)依賴包的,如果沒有的話可以使用npm自行安裝犬庇。如果使用的是我這篇文章所講的方法僧界,安裝時(shí)要安裝的包是@nuxtjs/axios
而不是axios
。
添加axios攔截器插件
與在Vue
中配置axios攔截器不同臭挽,在Nuxt.js
中配置axios攔截器使用的是Nuxt.js
的插件機(jī)制捂襟。要配置axios攔截器,我們需要在@/plugins
目錄下新建一個(gè)axios.js
(名稱無所謂欢峰,只要是.js
文件就可以了葬荷,但是該文件的名稱與配置文件中的名稱要對(duì)應(yīng))文件,在這個(gè)文件中進(jìn)行具體的配置:
由于前端才剛剛開始寫纽帖,所以這個(gè)文件的內(nèi)容非常不全面宠漩,僅供參考,以后如果添加了新的重要的邏輯懊直,會(huì)在后續(xù)文章中對(duì)這部分代碼進(jìn)行編輯扒吁。
@/plugins/axios.js
import { message } from 'ant-design-vue'
export default function ({ store, redirect, app: { $axios } }) {
// 后端接口地址
$axios.defaults.baseURL = 'http://127.0.0.1:19090/api/'
// Request攔截器:設(shè)置Token
$axios.onRequest((config) => {
// TODO 使用Vuex存儲(chǔ)Token,并做持久化處理
// config.headers.common['X-Token'] = store.state.token
})
// Error攔截器:出現(xiàn)錯(cuò)誤的時(shí)候被調(diào)用室囊,根據(jù)狀態(tài)碼做對(duì)應(yīng)判斷并顯示全局Message
$axios.onError((error) => {
const code = parseInt(error.response && error.response.status)
switch (code) {
// 未登錄
case 401:
redirect('/login')
break
default:
break
}
// 使用Ant Design Vue的message模塊顯示異常信息
message.error(error.response.data.message, 5)
})
// Response攔截器:對(duì)正常返回的數(shù)據(jù)進(jìn)行處理
$axios.onResponse((response) => {
return response.data
})
}
$axios.onRequest
雕崩、$axios.onResponse
和$axios.onError
是Nuxt.js
提供的方法魁索,類似于直接使用axios時(shí)的$axios.interceptors.response.use
方法,可以在這些方法中完成對(duì)請(qǐng)求信息和響應(yīng)信息的判斷與處理晨逝。這些方法與直接使用axios最大的不同就是默認(rèn)情況下不必返回任何內(nèi)容(當(dāng)然蛾默,onResponse
還是需要返回相應(yīng)數(shù)據(jù)response.data
,也就是響應(yīng)體本身(JSON)捉貌,包括了message
和code
——當(dāng)然如果不需要這兩部分的話也可以返回response.data.data
)支鸡,例如onRequest
方法不必返回config
等。
下面貼出純凈版的代碼供大家使用:
@/plugins/axios.js
export default function ({ store, redirect, app: { $axios } }) {
$axios.onRequest((config) => {
})
$axios.onError((error) => {
})
$axios.onResponse((response) => {
})
}
使用Ant Design Vue顯示全局消息
由于我在初始化項(xiàng)目的時(shí)候UI框架選擇了
Ant Design Vue
趁窃,所以項(xiàng)目里直接就整合好了Ant Design Vue
牧挣,需要的時(shí)候直接引入就可以了——但是目前項(xiàng)目中沒有配置按需引入,所以前端性能可能會(huì)不太好醒陆,最后會(huì)配置一下按需引入瀑构。如果沒有選擇的話,可以使用npm進(jìn)行安裝刨摩,再使用Nuxt插件進(jìn)行整合寺晌。
可以看到我的代碼中有這么一句:
// 使用Ant Design Vue的message模塊顯示異常信息
message.error(error.response.data.message, 5)
因?yàn)閷?duì)于錯(cuò)誤信息,個(gè)人認(rèn)為使用全局顯示的方法比組件內(nèi)單獨(dú)顯示更好澡刹,因?yàn)檫@樣可以更方便地做全局處理(當(dāng)然也有人不這么認(rèn)為呻征,那樣的話可以在組件內(nèi)單獨(dú)使用Alert
組件),所以每當(dāng)請(qǐng)求出現(xiàn)錯(cuò)誤的時(shí)候罢浇,都需要全局顯示這樣一條錯(cuò)誤信息陆赋。與在組件內(nèi)顯示錯(cuò)誤信息使用this.$message.error
等不同,Ant Design Vue
也給出了全局顯示錯(cuò)誤信息的方法:
組件提供了一些靜態(tài)方法嚷闭,使用方式和參數(shù)如下:
message.success(content, [duration], onClose)
message.error(content, [duration], onClose)
message.info(content, [duration], onClose)
message.warning(content, [duration], onClose)
message.warn(content, [duration], onClose)
// alias of warningmessage.loading(content, [duration], onClose)
——Ant Design Vue官方文檔
記得使用之前先導(dǎo)入message模塊:
import { message } from 'ant-design-vue'
效果:
在Nuxt.js中配置插件
有了插件之后還需要在Nuxt的配置文件nuxt.config.js
中進(jìn)行配置才能真正生效:
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
'@/plugins/antd-ui',
'@/plugins/axios'
],
注意這里的文件名是剛剛在@/plugins
目錄下新建.js
文件時(shí)所使用的文件名攒岛。我的文件名是axios.js
所以這里填寫@/plugins/axios
,如果是axios-config.js
就需要填寫@/plugins/axios-config
胞锰,以此類推灾锯。
組件內(nèi)使用
一般方法
以登錄為例:
@/pages/login.vue
methods: {
login () {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.$axios.post('/login', {
username: this.form.username,
password: this.form.password
}).then((res) => {
// TODO 登錄后的Token保存、跳轉(zhuǎn)等操作
this.$message.success(res.data.username, 5)
})
} else {
return false
}
})
},
reset () {
this.$refs.loginForm.resetFields()
}
}
與沒有axios攔截器的方法完全相同胜蛉。
效果:
asyncData()方法
以獲取一個(gè)測試接口的信息為例挠进,后端提供了一個(gè)測試接口/api/admin/test
,攜帶Token
進(jìn)行請(qǐng)求的話會(huì)返回成功信息:
后端代碼
// TODO admin測試
adminGroup.GET("/test", func(c *gin.Context) {
c.JSON(http.StatusOK, response.Success("/api/admin/test: Admin Test Succeeded!??"))
})
在@/pages/admin
目錄下新建一個(gè)測試用的頁面并在asyncData()
方法中請(qǐng)求對(duì)應(yīng)數(shù)據(jù)(在請(qǐng)求之前需要設(shè)置一下Token誊册。這里因?yàn)槲疫€沒整合Vuex
,直接寫死了暖璧,所以對(duì)應(yīng)的代碼沒什么參考價(jià)值案怯,下一篇文章中會(huì)講如何整合Vuex
以及在請(qǐng)求頭中設(shè)置Token
):
@/pages/admin/test.vue
<template>
<div>
{{ info }}
</div>
</template>
<script>
export default {
name: 'AdminTest',
asyncData (context) {
return context.$axios.get('/admin/test').then((res) => {
return {
info: res
}
})
},
data () {
return {
info: ''
}
}
}
</script>
效果: