一、axios
1. 功能
axios相比浏览器提供的fetch,有更多的优点 :
- 在浏览器中发送 XMLHttpRequests 请求
- 在 node.js 中发送 http请求
- 支持 Promise AP
- 拦截请求和响应 ( 原生fetch需要自己手动封装,比较麻烦 )
- 转换请求和响应数据
- ......
2. 安装
npm i axios
3. 常见的配置选项
4. 请求方式
01 - axios
axios(config)
import axios from 'axios';
axios({
// 请求方式
method: 'get',
// 请求地址
url: 'http:......'
}).then((res) => {
// 会返回一个promise,可以通过.then拿到结果
console.log('res', res.data);
});
02 - request
axios.request(config)
import axios from 'axios';
axios.request({
// 请求方式
method: 'get',
// 请求地址
url: 'http:......'
}).then((res) => {
// 会返回一个promise,可以通过.then拿到结果
console.log('res', res.data);
});
03 - get
axios.get(url[, config])
ps : 一样用法
axios.delete(url[, config])
axios.head(url[, config])
// 1. 可以在地址栏上直接跟上参数
axios.get('http://xxxxxx?id=123&name=coder').then((res) => {
console.log('res', res.data);
});
axios
.get('http://xxxxxx', {
// 2. 参数写在这里
params: {
id: 123,
name: 'coder'
}
})
.then((res) => {
console.log('res', res.data);
});
04 - post
axios.post(url[, data[, config]])
ps : 一样用法
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
import axios from 'axios';
axios
.post('http:xxxxxx', {
// 1. 把传递的参数直接写在这里
name: 'coder',
age: 18
})
.then((res) => {
console.log('res', res.data);
});
axios
.post('http:xxxxxx', {
// 2. 参数写在这里
data: {
name: 'star',
age: 16
}
})
.then((res) => {
console.log('res', res.data);
});
5. 配置公共的基础配置
// 1.baseURL
const baseURL = "http://123.123.123.123:8000"
// 给axios实例配置公共的基础配置
axios.defaults.baseURL = baseURL
axios.defaults.timeout = 10000
axios.defaults.headers = {}
// 2.使用
axios.get("/abc/a").then(res => {
console.log("res:", res.data)
})
// 3. 如果地址完整,就不会使用默认的配置
axios.get("http://456.456.456.456:8000/abc/a").then(res => {
console.log("res:", res.data)
})
6. 同时发送多个请求
// 等待两个请求都完成后,才会调用then
axios.all([
axios.get("http://123.111.222.333:9001/a"),
axios.get("http://123.222.333.444:9001/b")
]).then(res => {
console.log("res:", res)
})
// 底层的本质是调用了promise.all
7. axios创建实例
创建实例的目的 :
- 当我们从axios模块中导入对象时, 使用的实例是默认的实例
- 当给该实例设置一些默认配置时, 这些配置就被固定下来了
- 但是后续开发中, 某些配置可能会不太一样
- 比如某些请求需要使用特定的baseURL或者timeout等
- 这个时候, 可以创建新的实例, 并且传入属于该实例的配置信息.
如果项目比较大,用的数据并不是来自同一个服务器
方式一 : 创建各个实例,配置不同的baseURL
方式二 : 配置proxy,根据相同路由名称来映射至不同的网络
// 创建实例一
const instance1 = axios.create({
baseURL: "http://123.123.123.123:9001",
timeout: 6000,
headers: {}
})
// 发送网络请求
instance1.get("/abc", {
params: {
id: 1
}
}).then(res => {
console.log("res:", res.data)
})
// 创建实例二
const instance2 = axios.create({
baseURL: "http://456.456.456.456:8000",
timeout: 10000,
headers: {}
})
8. axios请求和响应拦截器
01 - 请求拦截
/**
对实例配置拦截
这里使用默认实例
*/
axios.interceptors.request.use((config) => {
console.log("请求成功的拦截")
/**
可以做
开始loading的动画
对原来的配置进行一些修改
header
认证登录: token/cookie
请求参数进行某些转化
*/
config.header['token'] = '123321fasdfagea'
// 最后要把被指返回出去
return config
}, (err) => {
console.log("请求失败的拦截")
return err
})
02 - 响应拦截
axios.interceptors.response.use((res) => {
console.log("响应成功的拦截")
// 1.结束loading的动画
// 2.对数据进行转化, 再返回数据
return res.data
}, (err) => {
console.log("响应失败的拦截:", err)
return err
})
二、axios - 普通版本封装
import axios from 'axios'
// 1. 创建一个类
class starRequest {
// 2. 构造函数中配置实例的基本配置
constructor(baseURL, timeout=10000) {
this.instance = axios.create({
baseURL,
timeout
})
}
// 3. 封装请求
request(config) {
return new Promise((resolve, reject) => {
this.instance.request(config).then(res => {
resolve(res.data)
}).catch(err => {
reject(err)
})
})
}
get(config) {
return this.request({ ...config, method: "get" })
}
post(config) {
return this.request({ ...config, method: "post" })
}
}
// 4. 导出
export default new starRequest("http://xxxxx:9001")
三、axios - TS版本封装
1. 简洁版
config/index.ts
/**
* 配置文件
*/
export const BASE_URL = 'http://xxx:8000'
export const TIMEOUT = 10000
request/index.ts
/**
* 封装axios请求
*/
// 1. 引入axios,导入类型
import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
class StarRequest {
// 实例类型 => AxiosInstance
private instance: AxiosInstance;
// config类型 => AxiosRequestConfig
constructor(config: AxiosRequestConfig) {
// 2. 创建实例
this.instance = axios.create(config);
}
// 3. 封装请求方法
request(config: AxiosRequestConfig) {
return this.instance.request(config);
}
get() {}
post() {}
}
export default StarRequest;
modules/home.ts
/**
* home模块: 用于存放home模块的接口
*/
import starRequest from '..';
export const getHomeData = () => {
return starRequest.request({
url: '/home/multidata'
});
};
index.ts
/**
* 统一的出口文件,方便其他地方引入
*/
import { BASE_URL, TIMEOUT } from './config';
import StarRequest from './request';
// 导出实例
const starRequest = new StarRequest({
baseURL: BASE_URL,
timeout: TIMEOUT
});
export default starRequest;
// 导出请求方法
export * from './modules/home';
使用
import { getHomeData } from './service';
getHomeData().then((res) => {
console.log(res.data);
});
2. 增加拦截器
全局拦截器 : 直接往instance上增加,每个请求都会触发
指定实例拦截器 : 给单独的实例再次增加拦截
ps : 拦截器可重复设置,不会覆盖
- 需求 : 给部分接口请求增加特定拦截
- 1. 给AxiosRequestConfig进行扩展
- 2. 实例创建的时候传递拦截
- 3. 构造函数中判断,增加新拦截
config/index.ts
/**
* 配置文件
*/
// 普通接口
export const BASE_URL = 'http://xxx:1111'
// 需要拦截的接口
export const BASE_URL2 = 'http://yyyyyy:2222/api'
export const TIMEOUT = 10000
request/type.ts
import { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
// 类型扩展,继承axios的类型,添加自定义的拦截器
export interface StarRequestConfig extends AxiosRequestConfig {
// 设置自定义的拦截器,可选
interceptors?: {
requestSuccessFn?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
requestFailureFn?: (err: any) => any;
responseSuccessFn?: (res: AxiosResponse) => AxiosResponse;
responseFailureFn?: (err: any) => any;
};
}
request/index.ts
/**
* 封装axios请求
*/
import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
// 1. 导入自定义扩展的类型,增加拦截器的可选参数
import type { StarRequestConfig } from './type';
class StarRequest {
private instance: AxiosInstance;
constructor(config: StarRequestConfig) {
this.instance = axios.create(config);
// 请求拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('全局请求拦截器-请求成功');
// 添加token
config.headers.token = 'token666';
return config;
},
(err) => {
console.log('全局请求拦截器-请求失败');
return err;
}
);
// 响应拦截器
this.instance.interceptors.response.use(
(res) => {
console.log('全局响应拦截器-响应成功');
return res.data;
},
(err) => {
console.log('全局响应拦截器-响应失败');
return err;
}
);
// 2. 针对特定的实例,添加拦截器 => 使用?可选链
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
);
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
);
}
request(config: AxiosRequestConfig) {
return this.instance.request(config);
}
get() {}
post() {}
}
export default StarRequest;
modules/extire.ts
import { starRequest2 } from '../index';
export const getEntiryList = () => {
return starRequest2.request({
url: '/entire/list',
params: {
offset: 0,
size: 20
}
});
};
index.ts
/**
* 统一的出口文件,方便其他地方引入
*/
import { BASE_URL, BASE_URL2, TIMEOUT } from './config';
import StarRequest from './request';
// 导出请求方法
export * from './modules/home';
export * from './modules/entire';
// 导出实例 => 普通接口
const starRequest = new StarRequest({
baseURL: BASE_URL,
timeout: TIMEOUT
});
export default starRequest;
// 导出实例2 => 添加特定拦截的接口
export const starRequest2 = new StarRequest({
baseURL: BASE_URL2,
timeout: TIMEOUT,
interceptors: {
requestSuccessFn: (config) => {
console.log('实例2请求拦截 - 请求成功');
return config;
},
requestFailureFn: (err) => {
console.log('实例2请求拦截 - 请求失败');
return err;
},
responseSuccessFn: (res) => {
console.log('实例2响应拦截 - 响应成功');
return res;
},
responseFailureFn: (err) => {
console.log('实例2响应拦截 - 响应失败');
return err;
}
}
});
使用
import { getHomeData, getEntiryList } from './service';
// 普通接口请求
getHomeData().then((res) => {
console.log(res);
});
// 特定拦截的接口请求
getEntiryList().then((res) => {
console.log(res);
});
3. 给返回数据加上类型
modules/home.ts
/**
* home模块: 用于存放home模块的接口
*/
import starRequest from '..';
// 1. 定义接口
interface IHomeData {
data: any;
returnCode: string;
success: boolean;
}
export const getHomeData = () => {
// 2. 使用泛型, 指定返回值类型
return starRequest.request<IHomeData>({
url: '/home/multidata'
});
};
modules/entire.ts
import { starRequest2 } from '../index';
// 1. 定义接口
interface IEntireList {
list: any[];
errorCode: number;
totalCount: number;
}
export const getEntiryList = () => {
// 2. 使用泛型, 指定返回值类型
return starRequest2.request<IEntireList>({
url: '/entire/list',
params: {
offset: 0,
size: 20
}
});
};
request/index.ts
/**
* 重载request方法,增加泛型, 用于指定返回值类型
* 注意是传入第二个泛型R,而不是第一个泛型T
* 默认泛型T为any,如果不指定,返回值类型为unknown => 无法使用
*/
request<T = any>(config: AxiosRequestConfig) {
return this.instance.request<any, T>(config);
}
get<T>(config: AxiosRequestConfig) {
return this.instance.request<any, T>({ ...config, method: 'GET' });
}
post<T>(config: AxiosRequestConfig) {
return this.instance.request<any, T>({ ...config, method: 'POST' });
}
delete<T>(config: AxiosRequestConfig) {
return this.instance.request<any, T>({ ...config, method: 'DELETE' });
}
patch<T>(config: AxiosRequestConfig) {
return this.instance.request<any, T>({ ...config, method: 'PATCH' });
}
效果
总结难点
- 拦截器控制
- 全局拦截器
- 实例拦截器
- 响应结果类型处理
- 泛型