1.前言
Web开发中,我们最常用的HTTP库就是Axios了,它的底层基于Ajax进行封装,在浏览器端和服务端都可以使用。如果大家对一些API不是很熟悉可以参考官方地址,或者中文社区。虽然现在网上已经有很多基于Axios封装的文章了,但是不妨碍我来接受社区大佬的“棒打”。
2.涉及到的业务场景
- 多环境:开发、测试、生产环境。
- 统一错误处理:401、404、500等错误。
- 断网、请求超时处理。
- 请求取消/请求拦截:防止重复请求发送到服务端,造成服务端压力。
- 请求权限:某些接口必须要有登录状态才可以访问。
当然涉及到的场景肯定不会只有这些,实际需要根据业务场景进行更好地配置。
3.如何封装
这里为了方便,我使用了ES6中的Class特性来抽象出一个HttpRequest
“类”(JS没有真正的类),它的属性有:
baseURL
:当前请求的根路径pending
: 一个对象,它使用请求API的URL来作为key来存储cancelToken
方法有:
getDefaultConfig
: 存放默认配置createAxiosInstance
: 创建Axios实例interceptors
: 配置拦截器cancelKeyManager
: cancelKey管理器handleRequestCancel
: 处理请求拦截和请求取消removeRequest
: 移除请求mergeOptions
: 工具函数,合并对象get
: get请求方法post
: post请求方法
接下来我们开始封装吧~
3.1 统一baseURL管理
其实这一块有很多方法来设置baseURL
,比如在process.env
定义NODE_ENV
,使用webpack.DefinePlugin
设置运行时的全局变量(这一部分Vue CLI 已经内置了),也可以使用Vue中的.env.[mode]
文件来设置(具体可以参考这里)。
我选择是前一种,在src
目录下创建一个config
文件夹存放项目运行时所需要的配置(编译时的配置在vue.config.js
配置)。
src/config/index.js
export default {
baseURL: {
dev: 'http://localhost:3000',
test: 'http://ip1:port1',
prod: 'http://ip2:port2'
}
}
这样我们只需要通过process.env.NODE_ENV
来应用不用的变量就可以啦。
src/utils/axios.js
import config from '@/config'
import HttpRequest from './HttpRequest'
// 根据当前环境获取API根目录
const baseURL = process.env.NODE_ENV === 'production' ? config.baseURL.prod : config.baseURL.dev
// 创建一个HtpRequest对象实例
const axios = new HttpRequest(baseURL)
export default axios
上面中的HttpRequest
是对axios的封装,接下来完善HttpRequest这个“类”。
3.2 定义数据属性
class HttpRequest {
// * 设置默认值为空方便使用devServer代理
constructor (baseURL = '') {
this.baseURL = baseURL
this.pending = {
} // 存储每次请求的cancel函数
}
}
上面我给了baseURL
一个默认空字符串,这是因为我们在开发阶段可能会用到代理,如果baseURL
不为空,那么代理设置的请求路径就会失效。
3.2 设置默认配置
/**
* axios默认配置
*
* @return {Object} axios默认配置
* @memberof HttpRequest
*/
getDefaultConfig () {
return {
baseURL: this.baseURL, // API根路径
headers: {
// 每次请求带上token
common: {
Authorization: `Bearer ${
store.state.token}` || ''
},
post: {
'Content-Type': 'application/json; charset=utf-8'
}
},
timeout: 10 * 1000 // 请求超时时间:10s后请求失败
}
}
上面是Axios的默认请求配置,这里我配置了项目常用的配置,这里说一下Authorization
,这个HTTP请求头的作用主要是鉴权,告诉服务器当前请求是否有权限。我这里使用了JWT鉴权机制
,所以需要携带token
,token放在vuex
和localstorage
中存储,所以需要引入Vuex
。
import store from '@/store'
此外,我还设置了post
默认实体请求头为application/json
,编码utf-8
,这是我们在Web开发中最常用的数据格式了,当然如果是表单类型的提交需要在实际请求中修改实体请求头为:'multipart/form-data'
或者'application/x-www-form-urlencoded'
,同时前者在请求时需要提交一个FormData对象(data:new FormData
),而后者可以引入qs
库把对象转为这种格式:key1=1value1&key2=value2
。
3.3 创建Axios实例
有了前面的准备,接下来我们需要借助Axios的create
方法来创建一个Axios实例,传入配置然后配置拦截器。
/**
*
* 创建Axios实例
* @param {Object} options 用户传进来的配置
* @return {Axios} 返回axios实例
* @memberof HttpRequest
*/
createAxiosInstance (options) {
const axiosInstance = axios.create()
// 将默认配置和用户设置的配置进行合并