axios是目前前端最常用的api请求库,这次就结合自己的使用经验分享一下的axios的封装。
官方文档:传送门
一、js封装
request.js:
/**
* 接口请求基础封装
*/
import axios from 'axios'
import { objToUrlParams } from './tools'
// 创建axios实例
const service = axios.create()
// 定义额外配置
let configMore
/**
* 数据请求 配置项
* @params {object} data 传参数据
* @params {boolean} codeList 可选,控制自行处理接口响应异常的code码列表,默认为空数组
* ......
*/
function request (config) {
// 获取额外配置参数
const { codeList } = config
configMore = {
codeList: codeList || []
}
// 额外的公共参数(根据公司情况自行调整)
const reqDataBase = {
token: '',
version: '',
}
// 合并公共参数
config.data = {
...reqDataBase,
...config.data
}
return service(config)
}
// 请求拦截
service.interceptors.request.use(
config => {
// 超时时间
config.timeout = config.timeout || 30000
// baseURL
if (!config.baseURL.match(/^http/)) {
// baseURL即公共部分的接口,我这里是/api/api
const baseUrlOrigin = config.baseURL || '/api/api'
// 这里的/proxy是接口代理的标识,用于拦截请求代理转发,解决跨域问题
config.baseURL = '/proxy' + baseUrlOrigin
}
// 数据传参使用表单形式
config.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
// 如果数据传参使用json形式,就改用下面一行
// config.headers['Content-Type'] = 'application/json; charset=utf-8'
// 请求方法,默认post
config.method = config.method || 'post'
// 请求参数,这里做了处理,无论是post请求还是get请求都可以统一使用data字段配置请求数据
if (config.method === 'post') {
config.data = config.data || {}
// 数据格式转换
config.transformRequest = [data => data && objToUrlParams(data)]
// 如果数据传参使用json形式,就改用下面一行
// config.transformRequest = [data => data && JSON.stringify(data)]
} else if (config.method === 'get') {
// get请求。将data数据转为params数据
config.params = config.data || config.params || {}
}
return config
},
err => {
Promise.reject(err)
}
)
// 响应拦截
service.interceptors.response.use(
response => {
const res = response.data
/**
* 自行控制返回数据
*/
if (configMore.codeList.length > 0) {
const myCodeList = configMore.codeList.map(v => (v + ''))
if (myCodeList.includes(res.code + '')) {
// 如果接口返回的code码在配置的codeList列表中,就直接返回数据,在接口调用处自行处理,方便DIY
return res
}
}
/**
* 默认自动控制返回数据
*/
// code为0,正常返回数据
if (res.code + '' === '0') {
return res
}
// code不为0,不符合预期,输出错误提示,res.error_msg为接口返回的错误信息,根据公司接口情况自行替换相应的字段
console.log(res.error_msg)
// code为-1,未登录或登录过期,跳转登录页
if (res.code + '' === '-1') {
// ...这里写跳转登录页的逻辑
}
// 抛出错误
return Promise.reject(res)
},
err => {
// 输出错误信息
console.log('Err:' + err.message)
return Promise.reject(err)
}
)
export default request
用到的工具 tools.js:
/**
* 判断是否是NaN
* @param {any} val 任意数据类型的数据
* @return {boolean}
*/
export function judgeNaN (val) {
return (typeof val) === 'number' && window.isNaN(val)
}
/**
* 对象参数序列化(过滤undefined和NaN,自动encode)
* @param {object} obj 对象参数
* @return {string} a=1&b=2&c=3
*/
export function objToUrlParams (obj) {
let str = ''
Object.keys(obj).forEach(v => {
const val = obj[v]
if (val !== undefined && !judgeNaN(val)) {
str += `${encodeURIComponent(v)}=${encodeURIComponent(val)}&`
}
})
return str.slice(0, -1)
}
- 个人觉得没必要再划分成axios.get和axios.post之类的方法,直接通过method参数指定请求方式就行了。
- 由于axios的get请求参数只能通过params来传递数据、post请求只能通过data来传递,所以我做了处理,统一通过data传递,在内部封装时再自动进行转换。
- 从axios的v0.19.0版本开始,axios的config不允许传递非官方支持的配置字段,所以我这里又包装了一层函数request来接受自定义的外部配置,通过外部作用域的configMore变量存储一些非官方支持的配置数据,然后在拦截器里面使用。
- 像接口code码的分类和判断以及代理标识等等不同公司环境可能不一样,不过都是大同小异,自行替换即可。
- 其实axios的封装是和公司前后端环境紧密关联的,所以没有一个绝对合适的js封装,我这里也是提供一些较为通用的配置参考。
二、使用示例
api.js
import request from './request'
export function getUserInfo (data) {
return request({
method: 'get',
url: '/app/getInfo',
data
})
}
export function getIndexData (data) {
return request({
method: 'post',
url: '/app/getIndex',
data,
codeList: [1000, 1100]
})
}
- 建议把所有api请求封装在一起,如上api.js,在页面里再引用api.js里的方法调用。
- 如有必要,可以将request.js作为基础封装,再基于此做几个二次封装来使用,便于不同类型接口的划分。