yiu-axios v1.0.60
Yiu Axios
基于axios的二次封装。
axios 库已经很好了,但是在发送请求时各种繁琐的细节,使得我们在项目使用 axios 的时候,始终需要二次封装。
yiu-axios 对 axios 进行一点二次封装,方便我们在项目中再次进行三次封装。
换种方式理解,你可以认为 yiu-axios 是使用 axios 为基础的 http 发送器。
以下 YC 为 YiuRequestConfig 的简写, AC 为 AxiosRequestConfig 的简写。
安装
npm:
npm i yiu-axiosyarn:
yarn add yiu-axios浏览器无其他依赖:
<script src="https://unpkg.com/yiu-axios@1.0.60/yiu-axios.iife.min.js"></script>浏览器有其他依赖:
<script src="https://unpkg.com/lodash@4.17.21/lodash.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/yiu-axios@1.0.60/yiu-axios.onlib.iife.min.js"></script>
<script>
console.log(YiuAxios)
// YiuAxios.yiuAxios.create()
// YiuAxios.yiuAxios.send()
</script>1.简单使用
1.1.yiuAxios.send()
这将使用 yiu-axios 中默认的 yiuAxios 实例。
import { yiuAxios } from 'yiu-axios'
import { MethodEnum } from 'yiu-axios/type'
yiuAxios.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
})1.2.yiuAxios.create()
这样你就可以维护多个 axios 和 yiu-axios 的实例了。
import { yiuAxios } from 'yiu-axios'
import { MethodEnum } from 'yiu-axios/type'
import axios from 'axios'
const defYiuAxios = yiuAxios.create({
baseURL: 'http://localhost:8080/',
timeout: 6000,
})
const defAxios = axios.create()
defYiuAxios.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
}, defAxios)1.3.yiuAxios.sendPromise()
返回一个Promise,方法主要用于并发请求。不然也没必要转Promise。
此时是没有返回取消方法的,YC 没有 success、error、finally方法。
import { yiuAxios } from 'yiu-axios'
import { MethodEnum } from 'yiu-axios/type'
Promise.all(
[
yiuAxios.sendPromise({
api: {
url: '/hello1',
method: MethodEnum.GET,
},
}),
yiuAxios.sendPromise({
api: {
url: '/hello2',
method: MethodEnum.GET,
},
})
]
)
.then(function (results) {
const acct = results[0];
const perm = results[1];
});1.4.yiuAxios.sendPromiseAndCanceler()
返回一个对象。
即又返回Promise,也返回取消函数。
取消函数由YC.cancel控制,此时YC 没有 success、error、finally方法。
{
promise: Promise<AxiosResponse<D>>,
canceler?: Canceler
}1.5.node
var YiuAxios = require("yiu-axios");
YiuAxios.yiuAxios.send({
api: {
url: '/hello',
method: 'get',
noMethod: true
},
debug: true,
contentType: 'application/json'
});2.配置优先级
AC>>YC>>默认YC>>默认ACyC.url>>YC.api.urlyC.method>>YC.api.method
以下案例最终将发送 http://localhost:8080/axios 的 GET 请求。
const defYiuAxios = yiuAxios.create({
// yiu-axios的默认配置
baseURL: 'http://localhost:8080/',
timeout: 6000,
})
const defAxios = axios.create({
// axios的默认配置
baseURL: 'http://localhost:8081/',
timeout: 6000,
})
defYiuAxios.send(
{
// yiu-axios的配置
api: {
url: '/yiu',
method: MethodEnum.GET,
},
},
defAxios,
{
// axios的配置
url: '/axios'
},
)3.yiu-axios的配置
3.1.api
将 url、method 提出来到 api 中是因为,我想将项目中的所有的接口都以这种方式的对象维护起来,方便同意管理。
而且 YC.api.method 中比 AC.method 多两种类型:POST_FORM_DATA、POST_FORM_URLENCODED。
如果你设置了这两种类型,最终都会发送 POST 请求, 并且会处理YC.contentType 和 YC.data 的编码。
注意:
- 不管
YC.contentType有没有值,YC.api.method的POST_FORM_DATA、POST_FORM_URLENCODED都将覆盖该值。 - 如果你设置了
AC.method,那么YC.api.method将不会作用在最终的请求上。
在 debug 模式下会检查 YC.api.method 的值是否规范。因为有些项目可能就是需要传入字符串,此时字符串如果乱填可能会影响 yiu-axios 判断。
如果你确定你的填值确实不在 yiu-axios 指定值内,那么可以将 noMethod 指定为 true ,从而取消控制台警告。
import { yiuAxios } from 'yiu-axios'
import { MethodEnum } from 'yiu-axios/type'
import axios from 'axios'
yiuAxios.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
}, axios.create())3.2.loading
这也是我编写 yiu-axios 的主要原因,项目中那么多 loading,全部自己维护实在太难了。
flag:- 可直接修改加载标识的对象。
- 类型受
YC的第二泛型影响。 - 比如
vue3的ref(false)、任意object。
key:- 不能直接修改的加载标识名。
- 比如一般的
boolean变量,这样的变量在方法中是不可修改的。 - 配合
obj一起使用。
obj:key标识的父对象。key本身不可修改,但是可以通过obj[key]修改key- 配合
key一起使用。
beforeSendFunc:- 在请求前要如何修改
loading - 参数依次为:
flag、obj、key。
- 在请求前要如何修改
finallySendFunc:- 在请求后要如何修改
loading - 参数依次为:
flag、obj、key。
- 在请求后要如何修改
3.2.1.obj + key
基本所有情况都可以使用这种obj + key方式解决。
最典型的场景就是 vue2 中 data 的 boolean 值, 你可以通过传入 this 和 booleanName 来修改 vue2 中的变量。
const yiuAxiosInstance = yiuAxios.create<any, { value: boolean }>({
baseURL: 'http://localhost:8080/',
timeout: 6000,
loading: {
beforeSendFunc: function ({ loadingObj, loadingKey }) {
if (loadingObj && loadingKey) {
loadingObj[loadingKey] = true
}
},
finallySendFunc: function ({ loadingObj, loadingKey }) {
if (loadingObj && loadingKey) {
loadingObj[loadingKey] = false
}
},
},
})
let loadingFlag = {
isLoading: false
}
yiuAxiosInstance.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
loading: {
obj: loadingFlag,
key: 'isLoading',
},
}, axios.create())3.2.2.flag
为什么有obj+key还需要flag? 如果 flag 是 vue3 的 Ref,那么是不是会轻松许多?
const yiuAxiosInstance = yiuAxios.create<any, { value: boolean }>({
baseURL: 'http://localhost:8080/',
timeout: 6000,
loading: {
beforeSendFunc: function ({ loading }) {
if (loading) {
loading.value = true
}
},
finallySendFunc: function ({ loading }) {
if (loading) {
loading.value = false
}
},
},
})
let loadingFlag = {
value: false
}
yiuAxiosInstance.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
loading: {
flag: loadingFlag,
},
}, axios.create())3.3.success、error、finally、isError
三个请求的必要事件。
isError 用于判断请求是否是失败的函数。一个返回200的请求在需求上不一定是成功的请求。 如果 isError 返回 true 那么将会调用 error,而不会调用 success。
yiuAxios.send({
api: {
url: '/hello',
method: MethodEnum.GET,
},
isError: (res) => {
return res?.data?.type === 'error'
},
success: (res) => {
console.log(res)
},
error: (err) => {
if (err.status && err.status >= 200 && err.status < 300) {
// 请求成功,被isError判定错误
} else if (error.response) {
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// `error.request` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
console.log(error.request);
} else {
// 发送请求时出了点问题
console.log('Error', error.message);
}
console.log(err)
},
finally: () => {
},
})3.4.tips
消息展示
type:消息展示的类型,由YC的第三个泛型控制。show:请求后是否展示消息anyObj:传给showFunc的任意对象,一般用于showFunc使用调用方特有的变量showFunc:展示成功消息的方法isSuccess:是否是成功消息type:消息类型result:请求后的结果content:消息内容title:消息标题anyObj:传入的showFunc对象
success:成功后的消息配置type:同上级type,比上级的优先级高show:同上级show,比上级的优先级高anyObj:传给showFunc的任意对象,一般用于showFunc使用调用方特有的变量,比上级的优先级高showFunc:同上级showFunc,比上级的优先级高type:消息类型result:请求成功后的结果content:消息内容title:消息标题anyObj:传入的showFunc对象
error:成功后的消息配置- 同
success
- 同
const yiuAxiosInstance = yiuAxios.create<any, any, 'console' | 'other'>({
tips: {
type: 'other',
show: true,
showFunc: ({ isSuccess, result, type, title, content, anyObj }) => {
const typeStr = isSuccess ? '成功' : '失败'
console.log('请求结果', result)
console.log('调用方传入的anyObj', anyObj)
switch (type) {
case 'other':
console.warn(`${typeStr}-${title}:${content}`)
break
case 'console':
default:
console.log(`${typeStr}-${title}:${content}`)
}
},
success: {
title: '成功默认标题',
content: '成功默认内容',
},
error: {
title: '失败默认标题',
content: '失败默认内容',
},
},
})
yiuAxiosInstance.send(
{
tips: { anyObj: 'Yiu' },
api: {
url: '/yiu',
method: MethodEnum.GET,
},
},
axios.create(),
)3.5.hook
请求的钩子函数。
beforeSend:- 发送请求前,返回
false则不继续处理后续代码。 - 参数
config为AC,即最终交给axios发送请求的配置对象,此处你可以修改它。 - 参数中存在
YC的字段,但是此时YC以转换成AC,所以请关注AC字段。
- 发送请求前,返回
beforeSuccess:- 执行
success前,返回false则不继续处理后续代码。 - 参数
res为请求成功后的结果。
- 执行
beforeError:- 执行
error前,返回false则不继续处理后续代码。 - 参数
err为请求失败后的结果。
- 执行
beforeFinally:- 执行
finally前,返回false则不继续处理后续代码。
- 执行
const yiuAxiosInstance = yiuAxios.create({
hook: {
beforeSend: (ac) => {
console.log('发送请求前')
console.log(ac)
ac.headers['AnyUpdate'] = 'AnyUpdate'
return true
},
beforeSuccess: (res) => {
console.log('执行 success 前')
console.log(res)
return true
},
beforeError: (err) => {
console.log('执行 error 前')
console.log(err)
return true
},
beforeFinally: () => {
console.log('执行 finally 前')
return true
},
},
})
yiuAxiosInstance.send(
{
api: {
url: '/yiu',
method: MethodEnum.GET,
},
},
axios.create(),
)3.6.pathHasBracket
YC.api.url 中是否有 {}。如果有的话,默认会报错,停止请求,因为这是链接参数的定义关键字。
如果该字段为true,那么请手动检查链接是否正确。
如果 pathHasBracket 和 pathData 配合使用还不能满足需求,那么直接使用 AC.url。
const yiuAxiosInstance = yiuAxios.create({
baseURL: 'http://localhost:8080',
pathHasBracket: true,
})
yiuAxiosInstance.send(
{
api: {
url: '/yiu?str={hello}',
method: MethodEnum.GET,
},
},
axios.create(),
)3.7.pathData
路径参数,该对象的 key 对应的 value 将自动映射到 路径中的 {key}。
YC.url 和 YC.api.url 是不允许有 {} 字符串存在的,如果有,将不会发送请求。
开启 debug 后,未发送的请求会在控制台打印它的错误。
如果需求有 {} 请配合 pathHasBracket 使用。
但是在检查 url 前还是会进行 pathData 的转换, 所以如果 pathHasBracket 和 pathData 配合使用还不能满足需求, 请手动拼接 AC.url,因为 yiu-axios
不关心 AC 的配置。
const yiuAxiosInstance = yiuAxios.create({
baseURL: 'http://localhost:8080',
debug: true,
})
yiuAxiosInstance.send(
{
api: {
url: '/yiu?str={hello}&num={num}',
method: MethodEnum.GET,
},
pathData: {
hello: 'Fidel',
num: 15,
},
},
axios.create(),
)3.8.lang & langFunc
lang:当前请求的语言。langFunc:语言配置方式。get:当lang不存在时才执行,获取当前语言。set:要将该语言设置在哪里的函数。- 第一个参数
YC, - 第二个参数
lang,如果YC.lang不存在,那么会执行YC.langFunc.get方法,然后再赋值给YC.lang。
- 第一个参数
只有 lang 或 get 方法值有效时,才会执行 set 方法。
const yiuAxiosInstance = yiuAxios.create({
baseURL: 'http://localhost:8080',
langFunc: {
get: () => {
// 比如从项目状态中获取语言信息
return 'zh-CN'
},
set: (yC, lang) => {
if (yC && lang) {
yC.headers['Accept-Language'] = lang
}
},
},
})
yiuAxiosInstance.send(
{
api: {
url: '/yiu',
method: MethodEnum.GET,
},
lang: 'en-US'
},
axios.create(),
)3.9.debug
当有些 YC 不满足要求时,请求将不发送。此时你可以使用 debug 来查看不发送的原因。
yiu-axios 会将错误打印在控制台,比如下面的 {}。
yiuAxios.send({
debug: true,
api: {
url: '/{id}/hello',
method: MethodEnum.GET,
},
}, axios.create())3.10.noToken & token & tokenFunc
noToken:这个请求是否需要设置token,如果为true将不会处理token和tokenFunc。token:该请求的token值。tokenFunc:token设置相关配置get:当token不存在时才执行,获取当前token。set:设置token的函数。- 第一个参数
YC, - 第二个参数
token,如果YC.token不存在,那么会执行YC.tokenFunc.get方法,然后再赋值给YC.token。
- 第一个参数
const yiuAxiosInstance = yiuAxios.create({
baseURL: 'http://localhost:8080',
tokenFunc: {
get: () => {
// 比如从项目状态中获取语言信息
return 'my-token'
},
set: (yC, token) => {
if (yC && token) {
yC.headers['Authorization'] = token
}
},
},
})
yiuAxiosInstance.send(
{
api: {
url: '/yiu',
method: MethodEnum.GET,
},
},
axios.create(),
)3.11.cancel
是否生成取消函数。
const cancelFunc = yiuAxios.send(
{
baseURL: 'http://localhost:8080',
api: {
url: '/yiu',
method: MethodEnum.GET,
},
cancel: true,
},
axios.create(),
)
setTimeout(() => {
if (cancelFunc) {
cancelFunc()
}
}, 1000)3.12.contentType & contentCharset
请求头类型,将自动设置到 YC.headers['Content-Type'] 中。
contentType:会被YC.aip.method影响。noContentTypeCheck:是否检查contentType。contentCharset:Content的编码,默认值:utf-8。
ContentTypeEnum 类型:
NONE:无contentTypeJSON:application/jsonHTML:text/htmlTEXT:text/plainXML:application/xmlJS:application/javascriptFORM_URLENCODED:application/x-www-form-urlencodedFORM_DATA:multipart/form-data
如果不是 ts 直接按照上面的字符串赋值赋值即可。
在 debug 模式下会检查 YC.contentType 的值是否规范。因为有些项目可能就是需要传入字符串,此时字符串如果乱填可能会影响 yiu-axios 判断。
如果你确定你的填值确实不在 yiu-axios 指定值内,那么可以将 noContentTypeCheck 指定为 true ,从而取消控制台警告。
import { ContentTypeEnum, MethodEnum } from 'yiu-axios/type'
yiuAxios.send(
{
baseURL: 'http://localhost:8080',
api: {
url: '/yiu',
method: MethodEnum.GET,
},
contentType: ContentTypeEnum.JSON,
},
axios.create(),
)3.13.upload
上传文件的配置。
上传文件时,请将YC.api.method或YC.contentType设置为FORM_DATA,否则不处理upload字段。
file:文件key:文件在FormData中的Key,默认为'file'name:上传的文件名
yiu-axios会将data和upload字段处理进FORM_DATA中。
// <input id="inputElement" name="file" type="file" accept="image/png, image/gif, image/jpeg" />
import { ContentTypeEnum, MethodEnum } from 'yiu-axios/type'
let inputElement = <HTMLInputElement>document.getElementById('inputElement')
if (inputElement && inputElement.files) {
const file = inputElement.files[0]
yiuAxios.send(
{
api: {
url: '/yiu',
method: MethodEnum.POST_FORM_DATA,
},
upload: { file },
data: { name: 'Fidel Yiu' },
},
axios.create(),
)
}3.14.noSend
不发送请求,而是在所有检测成功之后,在控制台打印出AC、YC。供开发者进行检查正确性。
yiuAxios.send(
{
baseURL: 'http://localhost:8080',
api: {
url: '/yiu',
method: MethodEnum.GET,
},
noSend: true,
},
axios.create(),
)4.三次封装
这里拿 vue3 的自动 loading 作为案例。
import { yiuAxios } from 'yiu-axios'
import axios, { Canceler } from 'axios'
import { YiuRequestConfig } from 'yiu-axios/type'
import { Ref } from 'vue'
const defYiuAxios = yiuAxios.create<any, Ref<boolean>>({
baseURL: 'http://localhost:8080/',
timeout: 6000,
loading: {
beforeSendFunc: function ({ loading }) {
if (loading) {
loading.value = true
}
},
finallySendFunc: function ({ loading }) {
if (loading) {
loading.value = false
}
},
},
})
const defAxios = axios.create()
export function yiuHttp<D = any, T = any>(c: YiuRequestConfig<D, T>): Canceler | undefined {
return defYiuAxios.send(c, defAxios)
}使用
import { defineComponent, ref } from 'vue'
import { MethodEnum } from 'yiu-axios/type'
import { yiuHttp } from '/@/utils/http'
export default defineComponent({
setup() {
const name = ref('Fidel')
const loading = ref(false)
yiuHttp({
api: {
method: MethodEnum.GET,
url: '/hello',
},
loading: {
flag: loading,
},
finally: () => {
name.value = 'Yiu'
},
})
return {
name,
loading,
}
},
})4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago