接口的访问可以说是前端开发中十分重要的一环了,那么当我们开发一个新项目时,到底该如何封装请求方法呢。相信很多小伙伴跟我一样,很少去站在工程的角度上考虑这个问题,下面我们一起讨论下吧!
首先,明确下需求,我们需要一个怎样的方法,想如何去使用这个方法呢。
初步想法是这样的:
- 省略大部分的配置,只关注于变量,比如,url、method、data
- 支持特殊的方法配置
- 能够进行统一的登录认证和错误处理
为了实现这个方法应该怎么做?
以axios为例,上述第三条axios已经帮我们实现了,axios中对请求和回复都提供了拦截器方法,需要注意的是,请求拦截其中的内容无法进行覆盖,所以只有一定需要发送的内容才在这里添加;回复拦截器中可以对各种状态码进行统一的处理和提示。看代码:
import axios from 'axios'
const service = axios.create()
service.interceptors.request.use(
config => {
// 预处理
// 访问接口的前一刻会走这里,只有那些每个接口都要发送的内容,才能在这里赋值
console.log("=========请求拦截器===============",config)
return config
},
error => {
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
// 预处理
// 接口返回的数据,会先来到这里,需要注意的是 只有2xx 范围内的状态码都会触发该函数
console.log("=========回复拦截器===============",response)
return response.data
},
error => {
// 其他的状态码会被视为异常发送到这里
return Promise.reject(error)
}
)
对于第1、2条问题就需要我们进行手动封装了
import { merge } from 'lodash'
// 每次访问的时候获取一下token
function getToken() {
return localStorage.getItem('token')
}
// service早就创建好了,只是调用的时候需要给service传不同的参数
// 所以采用闭包的形式进行封装。return一个接收新config的方法
function createRequest(service) {
return function (config) {
const token = getToken()
const defaultConfig = {
headers: {
Authorization: token ? `Bearer ${token}` : '',
'Content-Type': 'application/json'
},
timeout: 5000,
baseURL: import.meta.env.VITE_BASE_URL,
data: {}
}
// 注意这个地方是用了lodash的merge方法来进行定制化配置的覆盖,有的伙伴可能会想到了Object.assign
// 需要注意下这两个方法的区别
return service(merge(defaultConfig, config))
}
}
export default createRequest(service)
tips: merge与Object.assign的区别
通过以上两个部分,就完成了请求方法的基础封装。
那该如何使用呢?
建议考虑统一管理,不同的业务模块单独管理自己的请求接口,这样找的时候更方便一些。
文件结构如下
login.js
import request from "../request";
class Login {
constructor() {}
login(data) {
return request({
url: "/login",
method: "post",
data,
});
}
getUserInfo(params) {
return request({
url: "/getUserInfo",
method: "get",
params
})
}
}
const loginService = new Login();
export default loginService
request.js
import axios from 'axios'
import { merge } from 'lodash'
/**
* 需要的是一个request方法
* request(参数)能拿到请求结果
*
* 问题是如何生成request
*
* service()其实就是request
* 但是service需要有默认值和预处理操作
* 所以加一层,闭包
*/
const service = axios.create()
service.interceptors.request.use(
config => {
// 预处理
console.log("=========请求拦截器===============",config)
return config
},
error => {
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
// 预处理
console.log("=========回复拦截器===============",response)
return response.data
},
error => {
return Promise.reject(error)
}
)
function getToken() {
return localStorage.getItem('token')
}
function createRequest(service) {
return function (config) {
const token = getToken()
const defaultConfig = {
headers: {
Authorization: token ? `Bearer ${token}` : '',
'Content-Type': 'application/json'
},
timeout: 5000,
baseURL: import.meta.env.VITE_BASE_URL,
data: {}
}
return service(merge(defaultConfig, config))
}
}
export default createRequest(service)