UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

  • Git

  • Vite

  • 一些小工具

    • 二次封装axios
      • 前言
      • 搭建项目
      • readme
        • 使用
        • 自定义初始化配置
        • 函数类型
      • 构建
    • 妙用svg实现dom转image
    • 小程序实现全局状态管理
  • 算法

  • 服务器

  • HTTP

  • 技术
  • 一些小工具
hanhanbuku
2023-08-31
目录

二次封装axios

# 前言

对axios进行了一个简单的二次封装方便以后项目中快捷使用,顺便提一下怎么用rollup来构建ts项目

# 搭建项目

下面来搭建一个最简单的项目,因为只是一个简单的工具库,就不去使用脚手架搭建了直接新建一个目录然后进入目录中初始化一下项目。

pnpm init
1

这里用的是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 })
  }
}

1
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
}

1
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'

1
2

接下来我们去完善一下使用说明

# readme

# 使用

安装依赖

npm install @itachi3/ncaxios
1

引入工具

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')

1
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
}
1
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>

1
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,
}

1
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"
  }
} 

1
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的工具库的编写。

编辑 (opens new window)
上次更新: 2023/11/27, 14:12:31
深入理解vite核心原理
妙用svg实现dom转image

← 深入理解vite核心原理 妙用svg实现dom转image→

最近更新
01
小程序实现全局状态管理
07-09
02
前端检测更新,自动刷新网页
06-09
03
swiper渲染大量数据的优化方案
06-06
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式