让使用Axios变得优雅一点

本文最后更新于:2023年10月1日 晚上

介绍

基于axios和mockjs封装,请先安装

1
npm i axios mockjs

其他不介绍了,直接看使用好了

详细流程

创建文件结构(不重要,可自己改)

src

http

axios

apis(存放全部api的文件夹)

demoModule.js(demo模块的api)

...js(xxx模块的api)

interceptor.js(axios拦截器)

instances.js(axios实例)

mock

interceptor.js(mock拦截器)

以下文件内容中使用到了element-ui中的Message组件,可自行替换

axios 下的 interceptor.js(axios拦截器-核心1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* @author Xin-FAS
*/

import { clearToken, getToken } from '@/utils/handler-token'
import { Message } from 'element-ui'
import { router } from '@/router'

// 请求拦截器
const baseBeforeFilter = req => (req.headers.token = getToken(), req)
// 响应拦截器
const baseAfterFilter = ({
status,
statusText,
data: {
msg,
code,
data
}
}) => {
// 浏览器报错
if (status !== 200) return Promise.reject(statusText || '响应失败')
.catch(Message.error)
// 判断token过期
if (code === -1) {
clearToken()
return router.push('/info')
}
if (code !== 200) return Promise.reject(msg || '操作失败')
.catch(err => {
// 先统一处理一次,传递后续处理
Message.error(err)
return Promise.reject(err)
})
return data
}

export {
baseBeforeFilter,
baseAfterFilter
}

axios 下 instances.js(axios实例-核心2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import axios from 'axios'
import { baseAfterFilter, baseBeforeFilter } from '@/http/axios/interceptor'

/**
* @author Xin-FAS
*/
const defaultBaseOptions = {
timeout: 6000
}
const getType = val => Object.prototype.toString.call(val)
// 创建一个axios请求对象
const createAxiosInstance = (baseOptions, {
beforeFilter = baseBeforeFilter,
afterFilter = baseAfterFilter
} = {}) => {
const axiosInstance = axios.create(Object.assign({}, defaultBaseOptions, baseOptions))
axiosInstance.interceptors.request.use(beforeFilter)
axiosInstance.interceptors.response.use(afterFilter)
return options => {
const waitInstance = (instanceOptions = {}) => {
if (getType(instanceOptions) === '[object Function]')
return axiosInstance(options)
.then(instanceOptions, e => e)
const {
success,
error = e => e,
before = () => {},
after
} = instanceOptions
before()
return axiosInstance(options)
.then(success, error)
.finally(after)
}
// 为请求实例添加mock属性
waitInstance.mock = ({
success,
error,
before,
after
} = {}) => (
// 判断是否为mock实例
targetMockHTTP => getType(targetMockHTTP) === '[object Promise]' ||
targetMockHTTP({
success,
error,
before,
after
})
)(MockHTTP(options))
return waitInstance
}
}
// mock请求实例(mock实例必须第一个初始化,因为后续要添加这个实例)
const MockHTTP = createAxiosInstance({ baseURL: '/mock' })
// 基础公共请求实例
const BaseInstance = createAxiosInstance({ baseURL: '/api' })
// 本地资源请求实例
const LocalInstance = createAxiosInstance({ baseURL: '/src/assets' })
// 用户模块
const UserInstance = createAxiosInstance({ baseURL: '/api/user' })


export {
BaseInstance,
LocalInstance,
UserInstance,
}

axios 下 apis api文件(例子)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author Xin-FAS
*/
import { BaseInstance } from '@/http/axios/interceptor'

const DemoAPI = name => BaseInstance({
url: '/demo',
params: {
name
}
})

export {
DemoAPI
}

mock 下的 interceptor.js(例子)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author Xin-FAS
*/

import { mock } from 'mockjs'

mock(/\/mock\/demo/, 'get', {
code: 200,
msg: '操作成功!',
data: [
{
name: '123',
age: 123
},
{
name: '123',
age: 123
},
]
})

使用介绍

基础使用

此时有如 api 如:/api/demo,先定义接口方法(axios > apis > xxx.js

1
2
3
4
5
6
7
const DemoAPI = name => BaseInstance({
url: '/demo',
params: {
name
},
...
})

发出请求(真实地址:/api/demo?name=Xin-FAS

1
DemoAPI('Xin-FAS')()

返回的是正常的Promise对象,后续可以正常使用thenawait,但是不能使用catch去处理后续错误,理由在下面

发出使用mock的拦截请求(真实地址:/mock/demo?name=Xin-FAS

1
DemoAPI('Xin-FAS').mock()

几个内置的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DemoAPI('Xin-FAS')({
success (data) {
// 业务请求成功后,同Promise.success
},
error (msg) {
// 业务请求失败后,同Promise.catch
},
before () {
// 请求之前
},
after () {
// 请求之后(无论失败还是成功),同Promise.finally
}
})

关于业务上的错误后续处理

不能使用catch处理错误的原因在于,已经内置了错误处理,就算是DemoAPI('Xin-FAS')()直接使用也不会出现错误拦截,内置默认为.catch(e => e),所以要进行错误的后续操作需要写在error

1
2
3
4
5
6
7
DemoAPI('Xin-FAS')()
.then(res => {
// 正常执行
})
.catch(err => {
// 永远不会执行到
})

正确使用:

1
2
3
4
5
DemoAPI('Xin-FAS')({
error: msg => {
// ...
},
})

this 指向问题

在回调函数中使用普通函数存在this指向问题,this并不指向当前vue实例,所以要使用this需要使用箭头函数,如下写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 错误写法
DemoAPI('Xin-FAS')({
success (data) {
this.tableData = data
// ...
},
before () { this.loading = true },
after () { this.loading = false }
})
// 正确写法
DemoAPI('Xin-FAS')({
success: data => {
this.tableData = data
// ...
},
before: () => this.loading = true,
after: () => this.loading = false
})

简写形式

当只需要使用到成功处理时,如获取字典列表,可以在第二个方法中直接写一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
DemoAPI('Xin-FAS')({
success: data => {
this.tableData = data
// ...
},
})

// 简写为
DemoAPI('Xin-FAS')(data => {
this.tableData = data
// ...
})

await 使用演示

因为返回的是promise对象,所以await正常使用

1
const data = await DemoAPI('Xin-FAS')()
1
2
3
4
5
const data = await DemoAPI('Xin-FAS')({
error () {
// 错误后续处理
}
})

当出现await和回调一起使用时

1
2
3
4
5
6
const data = await DemoAPI('Xin-FAS')({
success (data) {
console.log(data) // 正常输出
}
})
console.log(data) // undefined

这是因为success中同then,需要返回后才能被后续接受

1
2
3
4
5
6
7
const data = await DemoAPI('Xin-FAS')({
success (data) {
console.log(data) // 正常输出
return data
}
})
console.log(data) // 正常输出
1
2
3
4
5
6
7
8
9
const data = DemoAPI('Xin-FAS')({
success (data) {
console.log(data) // 正常输出
return data
}
})
data.then(res => {
console.log(res) // 正常输出
})

封装代码中使用了thenfinally为空的情况(当参数不为函数时,自动转为e => e),解释如下:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#%E5%8F%82%E6%95%B0


让使用Axios变得优雅一点
https://xin-fas.github.io/2023/09/11/让使用Axios变得优雅一点/
作者
Xin-FAS
发布于
2023年9月11日
更新于
2023年10月1日
许可协议