搭建前端工程时,该如何封装请求方法呢

120 阅读2分钟

接口的访问可以说是前端开发中十分重要的一环了,那么当我们开发一个新项目时,到底该如何封装请求方法呢。相信很多小伙伴跟我一样,很少去站在工程的角度上考虑这个问题,下面我们一起讨论下吧!

首先,明确下需求,我们需要一个怎样的方法,想如何去使用这个方法呢。

初步想法是这样的:

  1. 省略大部分的配置,只关注于变量,比如,url、method、data
  2. 支持特殊的方法配置
  3. 能够进行统一的登录认证和错误处理
为了实现这个方法应该怎么做?

以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的区别

image-20241112220317150.png

通过以上两个部分,就完成了请求方法的基础封装。

那该如何使用呢?

建议考虑统一管理,不同的业务模块单独管理自己的请求接口,这样找的时候更方便一些。

文件结构如下

image-20241112220630480.png

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)