# [网络] Axios 网络请求库讲解

对于`axios`的使用，我已不陌生，但是想要进一步的了解一些细节，所以就打算写一篇博客，用于总结；这篇博客主要是`axios`和`ts`一起使用，并结合一些常见的业务场景来进行讲解

## **axios 实例的基础封装**

基础的步骤如下：

* 创建一个`axios instance`，配置其`baseURL`，有需要的话就配置一下代理
    
* 为`axios instance`添加拦截器，处理错误、请求、响应
    

## **axios 请求头注意事项**

默认情况下，`axios`发起请求会将`Conten-Type`设置为`json`格式，所以我们如果想要发送其他格式的请求的话，需要在`axios`实例发起请求的第三个参数中配置`headers`，如下：

```javascript
export const patchImport = async (payload: FormData) => {
  const { data } = await test2Instance.post<any>(
    "/enterprise/import", 
    payload, 
    {
      headers: {
        "Content-Type": "multipart/form-data" // 配置其他类型的请求
      }
    },
  )
  return data
} 
```

## **axios 拦截器的注意事项**

默认情况下，在后端返回数据经过`axios`的拦截器后，`axios`会将响应体数据默认再包裹一层`data`字段；所以我们可以在拦截器中直接返回`data`中的数据，减少层级，示例如下：

```javascript
instance.interceptors.response.use(
  (res: AxiosResponse) => {
    return res.data
  },
  (err: AxiosError) => {
    return Promise.reject(err)
  }
)
```

这样写之后，你会发现`axios`返回的数据的类型检验上是有问题的；因为`axios`并不能自动推断拦截器所返回的数据类型，而是需要我们手动在`axios`实例请求中声明类型，例如：

```javascript
/**
    这里 instance post 请求的返回类型实际上是 AxiosResponse<Person>
    即使你在拦截其中返回的是 res.data 而不是 res
*/
await instance.post<Person>
```

我们直接来看 `axios`对于其实例请求的类型声明：

```javascript
request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>
```

可以看出，所有类型的请求都会返回一个`Promise<R>`类型，并且`request`的第一个泛型参数是`T`，而第二个泛型参数`R`如果不手动声明，那么默认就会包上一层`AxiosResponse`，多出来的那一层`data`就是这么来的

所以，我们可以不管第一个泛型参数，而是直接给第二个参数赋一个返回的数据类型，就可以使得`axios`的类型校验争取

但是要注意：这里只是改变了类型声明而已，`axios`并不会因为类型声明而改变其返回的数据的层级结构，我们必须要与`axios`响应拦截器中的`return` [`res.data`](http://res.data)结合使用才是完全正确的

## **axios 登录过期重定向到登录页面**

这是一个很常见的业务场景，通常结合`router`的使用，让用户跳转到对应的界面就行了

首先我们需要判断响应状态码：

```javascript
instance.interceptors.response.use(
  (res: AxiosResponse) => {
    // 响应状态码为 2xx 的时候会走这个函数
    return res
  },
  (err: AxiosError) => {
    // 响应状态码在 2xx 以外的会走这个函数
    
    // 如果响应状态码为 401 未授权， 则导航到登录页
    if(err.response.status === 401) {
        // 执行导航重定向操作...
    }
    return Promise.reject(err)
  }
)
```

然后`vue`和`react`的导航有些不同

`vue`的`router`是可以直接从声明`router`对象的文件中引入，并可以直接在`.vue`组件外直接使用的

而`react`的`router`是通过`useRouter`获取的，由于`hook`只能在组件或其他`hook`中使用，所以还必须多下载一个`history`包才可以
