二次封装axios
# 前言
对axios进行了一个简单的二次封装方便以后项目中快捷使用,顺便提一下怎么用rollup来构建ts项目
# 搭建项目
下面来搭建一个最简单的项目,因为只是一个简单的工具库,就不去使用脚手架搭建了直接新建一个目录然后进入目录中初始化一下项目。
pnpm init
这里用的是pnpm
,其他的包管理工具也是同理。先生成一个package.json
再说
接下来新建一个src
目录并且新建两个子目录和一个index
文件
request
里面放的就是封装的代码,type
里面放的是类型,最后由index
统一进行暴露。
下面就开始写request
里的内容吧
这里我是的设计是这个库会内置一些常用
的功能,然后对常见的请求方式封装一下再暴露出去。并且为了方便扩展,可能还需要暴露一些和功能相关的信息出去。基于这种设计方案,最终我决定写成一个类
。
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import { RequestOpt, AxiosConfig, RequestConfig } from '../type'
export class NcRequest {
public instance: AxiosInstance
private cancelRequestSourceList: Record<string, AbortController> = {} // 存放所有取消请求方法的集合
constructor(options?: RequestOpt) {
this.instance = axios.create({
timeout: 10000,
...options?.base,
})
// 通用的请求拦截
this.instance.interceptors.request.use((config: AxiosConfig)=>{
if (config.url) {
if (this.cancelRequestSourceList[config.url]) {
this.cancelRequestSourceList[config.url].abort()
}
const controller = new AbortController()
config.signal = controller.signal
this.cancelRequestSourceList[config.url] = controller
}
config.headers = Object.assign(config.headers, config.mergeHeaders)
return options?.interceptReqSuccess ? options.interceptReqSuccess(config) : config
}, (error)=>{
return options?.interceptReqError ? options.interceptReqError(error) : Promise.reject(error)
})
// 通用的相应拦截
this.instance.interceptors.response.use((response: AxiosResponse)=>{
const url = response.config.url || ''
if (url) {
if (this.cancelRequestSourceList[url]) {
delete this.cancelRequestSourceList[url]
}
}
return options?.interceptResSuccess ? options.interceptResSuccess(response) : response
}, (error)=>{
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '错误请求'
break
case 401:
error.message = '未授权,请重新登录'
break
case 403:
error.message = '拒绝访问'
break
case 404:
error.message = '请求错误,未找到该资源'
break
case 405:
error.message = '请求方法未允许'
break
case 408:
error.message = '请求超时'
break
case 500:
error.message = '服务器端出错'
break
case 501:
error.message = '网络未实现'
break
case 502:
error.message = '网络错误'
break
case 503:
error.message = '服务不可用'
break
case 504:
error.message = '网络超时'
break
case 505:
error.message = 'http版本不支持该请求'
break
default:
error.message = `连接错误${error.response.status}`
}
} else {
error.message = '连接到服务器失败'
}
return options?.interceptReqError ? options.interceptReqError(error) : Promise.reject(error)
})
}
public get<T, D>(
url: string,
params?: T,
config?: RequestConfig,
): Promise<D> {
return this.instance.get(url, { params, ...config })
}
public post<T, D>(
url: string,
data?: T,
config?: RequestConfig,
): Promise<D> {
return this.instance.post(url, data, { ...config })
}
public put<T, D>(
url: string,
data?: T,
config?: RequestConfig,
): Promise<D> {
return this.instance.put(url, data, { ...config })
}
public delete<T, D>(
url: string,
params?: T,
config?: RequestConfig,
): Promise<D> {
return this.instance.delete(url, { params, ...config })
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
里面代码很简答,就是对几个常见的请求方式做了一个封装,然后在拦截器里面对重复请求做了一个去重
的处理。其他的功能暂时还没想好就没写了。
因为是对axios
的二次封装,所以axios
支持的很多东西我们肯定要开放出去,并且对一些自由度较高的功能不应该做过度的封装。我觉得这个库只需要提供一些小功能就行了,在使用上最好还是保持axios
的传统。他支持什么我们就也开放什么,不做过度封装。
下面是type
,里面也列出了我们这个库可以接收哪些参数
import { InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig } from 'axios'
export interface AxiosConfig extends InternalAxiosRequestConfig{
// 合并请求头
mergeHeaders?: Record<string, unknown>
[key: string]: any
}
export interface RequestOpt{
base?: BaseOpt
interceptReqSuccess?: (config: AxiosConfig) => AxiosConfig // 请求拦截器成功的回调
interceptReqError?: (err: any) => Promise<never> // 请求拦截器失败的回调
interceptResSuccess?: (response: AxiosResponse) => AxiosResponse // 相应拦截器成功的回调
interceptResError?: (err: any) => Promise<never> // 相应拦截器失败的回调
}
export interface BaseOpt{
baseURL?: string
timeout?: number
headers?: Record<string, any>
}
export interface RequestConfig extends AxiosRequestConfig{
mergeHeaders?: Record<string, any>
[key: string]: any
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
index.js
export * from './request/index'
2
接下来我们去完善一下使用说明
# readme
# 使用
安装依赖
npm install @itachi3/ncaxios
引入工具
import { NcRequest } from '@itachi3/ncaxios'
const request = new NcRequest()
// request封装了get,post,put,delete四种请求方式
request.get('url',data,{}).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
request.post('url')
request.put('url')
request.delete('url')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 自定义初始化配置
const request = new NcRequest(options:RequestOpt)
export interface RequestOpt{
base?: BaseOpt
interceptReqSuccess?: (config: AxiosConfig) => AxiosConfig // 请求拦截器成功的回调
interceptReqError?: (err: any) => Promise<never> // 请求拦截器失败的回调
interceptResSuccess?: (response: AxiosResponse) => AxiosResponse // 相应拦截器成功的回调
interceptResError?: (err: any) => Promise<never> // 相应拦截器失败的回调
}
export interface BaseOpt{
baseURL?: string
timeout?: number
headers?: Record<string, any>
}
export interface RequestConfig extends AxiosRequestConfig{
mergeHeaders?: Record<string, any>
[key: string]: any
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 函数类型
type get<T, D> = (url: string, params?: T, config?: RequestConfig,)=> Promise<D>
type post<T, D> = (url: string, params?: T, config?: RequestConfig,)=> Promise<D>
type put<T, D> = (url: string, params?: T, config?: RequestConfig,)=> Promise<D>
type delete<T, D> = (url: string, params?: T, config?: RequestConfig,)=> Promise<D>
2
3
4
5
6
7
8
# 构建
这里我们用rollup
去进行构建,rollup
非常的轻量化,构建简单的东西时无需配置太多。
首先安装一下rollup
,安装好后在根目录新建一个rollup.config.js
import resolve from '@rollup/plugin-node-resolve' // 在 node_modules 中找到并捆绑第三方依赖项
import alias from '@rollup/plugin-alias' // 定义和解析捆绑包依赖项的别名
import typescript from 'rollup-plugin-typescript2' // Rollup 和 Typescript 之间的集成
import commonjs from '@rollup/plugin-commonjs' // 将 CommonJS 模块转换为 ES6
import json from '@rollup/plugin-json' // 将 .json 文件转换为 ES6 模块
import { terser } from 'rollup-plugin-terser' // 压缩代码
import del from 'rollup-plugin-delete' // 删除之前的构建产物
const plugins = [
resolve({ browser: true }),
alias(),
json(),
commonjs(),
typescript(),
terser(),
del({ targets: 'dist/*' }),
]
export default {
input: 'src/index.ts',
output: [
{
file: 'dist/index.js',
format: 'esm',
},
{
file: 'dist/index.cjs',
format: 'cjs',
},
],
plugins,
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
主要就是用到很多插件,因为是基于axios
的所以在构建时也需要去处理一些axios
的代码问题。基本就是见招拆招了,最开始我只用了一个ts
的插件,然后报一个错加一个插件加着加着就这么多了...
- 注意这里不需要用
esbuild
插件去打包,我试过了打出来会保留axios
的node
模块代码引用,在浏览器中用不了
最后我们去修改一下package.json
{
"name": "@itachi3/ncaxios",
"version": "1.0.3",
"description": "基于axios二次封装的工具库",
"main": "dist/index.cjs", // 配置require导入的文件路径
"module": "dist/index.js", // 配置import导入的文件路径
"type": "module",
"types": "./dist/index.d.ts", // 配置一下类型提示文件,使用方需要用到,不然ts会报错
"scripts": {
"build": "pnpm rollup -c",
"test": "echo \"Error: no test specified\" && exit 1"
},
"files": [ // 配置上传至npm的内容
"dist/*",
"README.md"
],
"exports": { // 配置导出文件,这个配置和上面的main和module好像是重复了,会有一个权重的问题,应该是优先使用exports里的配置
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"keywords": [ // 配置npm搜索关键字
"axios",
"http",
"request"
],
"author": "wyh",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.22.10",
"@rollup/plugin-alias": "^5.0.0",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.4",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.1.0",
"rollup": "^3.28.0",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-dts": "^5.3.1",
"rollup-plugin-esbuild": "^5.0.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.35.0",
"tslib": "^2.6.1"
},
"peerDependencies": { // 库必须基于axios
"axios": "^1.3.5"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
写到这里包就可以发布了,但是咱们得规范一下,搞一个changelog
然后自动化发布这样才方便。
由于这个项目在写的时候是在monorepo
里,所以这里我才用changeset
来生成changelog
和发包。
关于changeset
的使用可以看另一篇文章,这里就不重复介绍怎么使用了。
至此我们就完成了一个ts+rollup
的工具库的编写。