vue-axios-optimize 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +91 -71
  2. package/index.js +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # vue-axios-optimize使用说明
2
2
 
3
- vue-axios-optimize是一个对axios请求优化的vue插件包,引入此包之后,并进行相关配置,即可轻松实现全局请求加载动画,重复请求时取消前面的请求或者阻止后面的请求,并且可实现请求并发数量控制。
3
+ vue-axios-optimize是一个对axios请求优化的vue插件包,引入此包之后,并进行相关配置,即可轻松实现全局请求加载动画,重复请求时取消前面的请求或者阻止后面的请求,并且可实现请求并发数量控制以及接口缓存。
4
+ [可阅读此文章 了解](https://mp.weixin.qq.com/s?__biz=MzUyMjkxNDc3Mg==&mid=2247484964&idx=1&sn=3cbd0fdcd5f8877acd1179df96fa5776&chksm=f9c5d89fceb2518917cb2e313d2b302a335f70ebf4999d570484b697264f1353d66730fff961&token=319626610&lang=zh_CN#rd)
4
5
 
5
6
  ## 安装
6
7
 
7
8
  ```shell
8
- npm install vue-axios-optimize
9
+ npm install vue-axios-optimize -S
9
10
  ```
10
11
 
11
12
  ## 引入
@@ -28,59 +29,47 @@ api目录大致如下
28
29
  > > >
29
30
  > > > axios-optimize.js
30
31
 
31
- **无配置无感知刷新token时** axios-optimize.js文件内容大致如下
32
+ axios-optimize.js文件内容大致如下
32
33
 
33
34
  ```js
34
35
  import axios from "axios"
35
36
  import instance from "@/api/axios"
36
- import axiosOptimize from "vue-axios-optimize"
37
- import store from "@/store"
38
-
39
- const AxiosOptimize = new axiosOptimize(axios, instance, {
40
- responseTypesStr: "arraybuffer,blob", // 当axios请求设置 responseType为arraybuffer或blob时,直接返回原始数据
41
- showLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量不为0时的回调函数
42
- store.commit("app/SET_LOADING_STATUS", true)
43
- },
44
- hideLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量为0时的回调函数
45
- store.commit("app/SET_LOADING_STATUS", false)
46
- },
47
- responseResultFun: (res) => { // 相应数据格式化
48
- return res.data // 业务上希望用到的数据
49
- }
50
- })
51
-
52
- export default AxiosOptimize
53
- ```
54
-
55
- **配置无感知刷新token时** axios-optimize.js文件内容大致如下
56
-
57
- ```js
58
- import axios from "axios"
59
- import instance from "@/api/axios"
60
- import axiosOptimize from "vue-axios-optimize"
37
+ import axiosOptimize from "@/api/newaxios"
61
38
  import store from "@/store"
62
39
  import {MessageBox} from "element-ui"
63
40
 
64
41
  const AxiosOptimize = new axiosOptimize(axios, instance, {
65
- responseTypesStr: "arraybuffer,blob", // 当axios请求设置 responseType为arraybuffer或blob时,直接返回原始数据
42
+ maxReqNum: 2, // 同时最多请求数量 选配 默认 4
43
+ cacheNum: 2, // 缓存数量 选配 默认10
44
+ responseTypesStr: "arraybuffer,blob", // 当axios请求设置 responseType为arraybuffer或blob时,直接返回原始数据 选配
66
45
  showLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量不为0时的回调函数
67
46
  store.commit("app/SET_LOADING_STATUS", true)
68
47
  },
69
48
  hideLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量为0时的回调函数
70
49
  store.commit("app/SET_LOADING_STATUS", false)
71
50
  },
72
- openRefresh: true, // 启用无感知刷新token的开关
73
- responseResultFun: (res) => { // 相应数据格式化
51
+ responseResultFun: (res) => { // 响应数据格式化
74
52
  return res.data
75
53
  },
76
- refreshApiUrl: "/xiaobuAdmin/refreshToken", // 刷新tokenApi的 url
54
+ openRefresh: true, // 是否开启无感知刷新token 以下配置均与无感知刷新token有关 如无需配置无感知刷新token 可忽略
55
+ code: "code", // 判断响应码的字段标识 默认code
56
+ accessToken: "accessToken", // headers存放accessToken的字段名 默认 accessToken 用于判断当前接口是否使用的最新的token
57
+ accessTokenExpirationCode: 401, // accessToken过期时 接口响应状态码 默认 数字类型 401
58
+ refreshTokenExpirationCode: 403, // 刷新token接口请求报错时的 响应状态码 默认 数字类型 403
59
+ setAccessTokenFun: (config, accessToken) => { // 设置接口请求token的 并返回新的config 默认为 config.headers.Authorization = "Bearer " + accessToken
60
+ config.headers.Authorization = "Bearer " + accessToken
61
+ return config
62
+ },
77
63
  getRefreshTokenFun: () => { // 获取refreshToken的方法
78
64
  return store.getters.refreshToken
79
65
  },
80
- refreshTokenStore: (refreshToken) => { // 调用刷新token的方法 并将获取到的数据存储到cookie 及axios.js用到token的地方
66
+ getAccessTokenFun: () => { // 获取accessToken的方法
67
+ return store.getters.accessToken
68
+ },
69
+ refreshTokenStore: (refreshToken) => { // 调用刷新token的方法 并将获取到的数据存储到cookie 且需要将接口返回的数据 resolve(data)
81
70
  return store.dispatch("user/refreshToken", refreshToken)
82
71
  },
83
- reloginFun: async(response) => {
72
+ reloginFun: async(response) => { // 刷新token接口也报错后的处理逻辑 建议进行类似如下方法处理
84
73
  // 重新登录方法 弹框提示 点击确认后 建议清除 缓存中token等信息后 刷新页面
85
74
  MessageBox.alert(`${response.message}`, "提示", {
86
75
  confirmButtonText: "OK",
@@ -100,19 +89,18 @@ const AxiosOptimize = new axiosOptimize(axios, instance, {
100
89
  export default AxiosOptimize
101
90
  ```
102
91
 
103
-
104
-
105
92
  axios.js的内容需要做些细微的更改 大致内容如下
106
93
 
107
94
  ```js
95
+
108
96
  import axios from "axios"
109
97
  import Message from "@/plugins/reset-message.js"
110
98
  import store from "@/store"
111
99
 
112
100
  // 创建axios实例
113
101
  const service = axios.create({
114
- baseURL: window.configObj.baseUrl || process.env.VUE_APP_BASE_URL,
115
- timeout: window.configObj.timeout || process.env.VUE_APP_TIME_OUT // 请求超时时间
102
+ baseURL: XXX,
103
+ timeout: XXX // 请求超时时间
116
104
  })
117
105
 
118
106
  // request拦截器
@@ -120,12 +108,16 @@ service.interceptors.request.use(
120
108
  config => {
121
109
  const accessToken = store.getters.accessToken
122
110
  if (accessToken) {
111
+ // 配置无感知刷新token时 建议请求头多加个 accessToken, 如为其他字段可 配置 为其他字段 用于判断当前接口携带的accessToken
112
+ config.headers.accessToken = accessToken
113
+ // 用于做接口权限标识
123
114
  config.headers.Authorization = "Bearer " + accessToken
124
115
  }
125
116
 
126
117
  return config
127
118
  },
128
119
  error => {
120
+ // 需要return
129
121
  return Promise.reject(error)
130
122
  }
131
123
  )
@@ -142,28 +134,42 @@ service.interceptors.response.use(
142
134
  type: "error",
143
135
  duration: 5 * 1000
144
136
  })
137
+ // 错误响应时,多返回一个config 和 responseErr设置为true 让vue-axios-optimize 拿到config 以及知道 这是一个错误的响应数据
145
138
  return { ...data, config, responseErr: true}
146
139
  } else {
147
140
  // 二进制数据则直接返回
148
141
  if (request.responseType === "blob" || request.responseType === "arraybuffer"){
142
+ // 直接返回数据
149
143
  return response
150
144
  }
151
145
  if (data.code === 200) {
146
+ // 正常响应时,多返回一个config 让vue-axios-optimize 拿到config
152
147
  return { ...data, config}
153
148
  }
149
+ if (data.code === 401 || data.code === 403) {
150
+ // 错误响应时,多返回一个config 和 responseErr设置为true 让vue-axios-optimize 拿到config 以及知道 这是一个错误的响应数据
151
+ return { ...data, config, responseErr: true}
152
+ }
153
+ Message({
154
+ message: data.message || "Error",
155
+ type: "error",
156
+ duration: 5 * 1000
157
+ })
158
+ // 错误响应时,多返回一个config 和 responseErr设置为true 让vue-axios-optimize 拿到config 以及知道 这是一个错误的响应数据
154
159
  return { ...data, config, responseErr: true}
155
160
  }
156
161
  },
157
162
  error => {
158
- if (error.name === "AxiosError") {
163
+ // 取消请求时 不报错误信息
164
+ if (error.name !== "CanceledError") {
159
165
  Message.error(error.message)
160
166
  }
167
+ // 需要return
161
168
  return Promise.reject(error)
162
169
  }
163
170
  )
164
171
 
165
172
  export default service
166
-
167
173
  ```
168
174
 
169
175
 
@@ -171,47 +177,51 @@ export default service
171
177
  接口处使用,如commo-api.js 大致如下
172
178
 
173
179
  ```js
174
- import axiosOptimize from '@/api/axios-optimize.js'
175
-
180
+ import axiosRequest from "@/api/axios-optimize"
176
181
  // 需要配置 请求接口时 不加载全局动画 则 配置 noShowLoading: true
177
- // 需要配置 重复请求时 取消前面的请求 则 配置 preventDuplicateRequestsType: "cancel"
178
- // 需要配置 重复请求时 禁用后面的请求 则 配置 preventDuplicateRequestsType: "prevent"
179
- // 需要配置 接口缓存的 则 配置 cache: true 否则 配置为false 或者不配置 配置后该接口将进行缓存 (V2.0.1及以上)
182
+ // 需要配置 缓存的接口 则 配置 cache: true 否则 配置为 false 或 不配置
183
+ // 需要配置 重复请求时 取消前面的请求 则 配置 preventDuplicateRequestsType: "cancel"
184
+ // 需要配置 重复请求时 禁用后面的请求 则 配置 preventDuplicateRequestsType: "prevent"
185
+ // 需要配置 全路经(包括入参数据)为接口唯一标识的 则配置 fullPath: true 否则 配置为 false 或 不配置 否则仅仅以URL为唯一标识
186
+ // 当需要配置无感知刷新token时,对于刷新token接口需要配置 isRefreshToken 为true 否则配置为 false 或者不 配置
180
187
 
181
188
  // get 请求demo
182
189
  export function getDemo(
183
190
  data = {},
184
191
  config = {
185
192
  noShowLoading: true, // 配置不展示加载动画
186
- preventDuplicateRequestsType: "cancel" // 配置重复请求时 取消前面的请求
193
+ preventDuplicateRequestsType: "cancel" // 配置重复请求时 取消前面的请求
187
194
  }
188
195
  ) {
189
- return axiosOptimize.get(`/xiaobu-admin/getDemo`, { params: data, ...config })
196
+ return axiosRequest.get(`/xiaobu-admin/getDemo`, { params: data, ...config })
190
197
  }
191
198
 
192
199
  // post 请求demo
193
200
  export function postDemo(data = {}, config = {}) {
194
- return axiosOptimize.post(`/xiaobu-admin/postDemo`, data, config)
201
+ return axiosRequest.post(`/xiaobu-admin/postDemo`, data, config)
195
202
  }
196
203
 
197
204
  // delete 请求demo1 使用data作为参数 参数不展示在 请求路径上
198
205
  export function deleteDemo1(data = {}, config = {}) {
199
- return axiosOptimize.delete(`/xiaobu-admin/deleteDemo1`, { data, ...config })
206
+ return axiosRequest.delete(`/xiaobu-admin/deleteDemo1`, { data, ...config })
200
207
  }
201
208
 
202
209
  // delete 请求demo2 使用params作为参数 参数展示在 请求路径上
203
210
  export function deleteDemo2(data = {}, config = {}) {
204
- return axiosOptimize.delete(`/xiaobu-admin/deleteDemo2`, { params: data, ...config })
211
+ return axiosRequest.delete(`/xiaobu-admin/deleteDemo2`, {
212
+ params: data,
213
+ ...config
214
+ })
205
215
  }
206
216
 
207
217
  // put 请求demo
208
- export function putDemo1(data = {}, config = {}) {
209
- return axiosOptimize.put(`/xiaobu-admin/putDemo`, data, config)
218
+ export function putDemo(data = {}, config = {}) {
219
+ return axiosRequest.put(`/xiaobu-admin/putDemo`, data, config)
210
220
  }
211
221
 
212
222
  // patch 请求demo
213
- export function patchDemo1(data = {}, config = {}) {
214
- return axiosOptimize.patch(`/xiaobu-admin/patchDemo`, data, config)
223
+ export function patchDemo(data = {}, config = {}) {
224
+ return axiosRequest.patch(`/xiaobu-admin/patchDemo`, data, config)
215
225
  }
216
226
 
217
227
  ```
@@ -226,22 +236,24 @@ export function patchDemo1(data = {}, config = {}) {
226
236
 
227
237
  ## options配置
228
238
 
229
- | 参数 | 类型 | 说明 | 是否必传 | 默认值 |
230
- | -------------------------------------------------------- | -------- | ------------------------------------------------------------ | ------------------------- | ----------------------------------- |
231
- | showLoadingFun(config, requestingNum) | Function | 展示动画,config为该请求的config,requestingNum为目前正在请求几个接口,此处可执行一些展示全局动画的操作 | 否 | |
232
- | hideLoadingFun(config, requestingNum) | Function | 隐藏动画,config为该请求的config,requestingNum为目前正在请求几个接口,此时应该为0,此处可执行关闭全局动画的操作 | 否 | |
233
- | openRefresh | Boolean | 是否开启无感知刷新token | 否 | false |
234
- | refreshApiUrl | String | 请求刷新token接口的api地址 如/xiaobu-admin/refresh-token | 当openRefresh为true时必传 | 无 |
235
- | getRefreshTokenFun() | Function | 获取当前项目的 refreshToken方法, 记得 return 回去 | 当openRefresh为true时必传 | |
236
- | reloginFun(response) | Function | response 为axios实例中返回的数据,此处建议执行清除token相关缓存并弹框提示,点击确认进行刷新页面操作 | 当openRefresh为true时必传 | 无 |
237
- | refreshTokenStore(refreshToken) | Function | refreshToken为当前浏览器缓存的refreshToken,需要返回一个Promise,利用此refreshToken调用接口获取新的accessToken 并设置。v1.0.6及以上无需返回任何数据 | 当openRefresh为true时必传 | 无 |
238
- | setAccessTokenFun(config, accessToken) (v1.0.8已废除) | Function | config为该请求的config,accessToken 为新获取的accessToken, 此处需要将accessToken设置到请求头中 | 当openRefresh为true时必传 | 无 |
239
- | responseResultFun(res) | Function | res为axios实例返回的res,格式化接口请求成功后返回的数据 | 非必传 | axios实例返回的res |
240
- | formatAccessTokenFun(data) v1.0.6已废除 | Function | dataresponseResultFun报错时返回的res | 非必传 | axios实例返回的res.data.accessToken |
241
- | accessTokenExpirationCode | number | 配置accessToken过期返回的业务状态码 | 非必传 | 401 |
242
- | refreshTokenExpirationCode | number | 配置refreshToken过期返回的业务状态码 | 非必传 | 403 |
243
- | responseTypesStr(v1.0.2) | String | 配置数据返回类型,实现当此类型时返回原封不动的响应数据的data数据 | 非必传 | |
244
- | maxReqNum (v2.0.0) | Number | 最大同时请求数量 | 非必传 | 4 |
239
+ | 参数 | 类型 | 说明 | 是否必传 | 默认值 |
240
+ | ------------------------------------------------------------ | -------- | ------------------------------------------------------------ | ------------------------- | ------------------------------------------------------------ |
241
+ | maxReqNum | Number | 同时最大可请求数量,高并发任务时,可同时进行的任务处理数量 | 否 | 4 |
242
+ | cacheNum | Number | 可缓存的键值对个数 | 否 | 10 |
243
+ | showLoadingFun(config, requestingNum) | Function | 展示动画,config为该请求的config,requestingNum为目前正在请求几个接口,此处可执行一些展示全局动画的操作 | 否 | |
244
+ | hideLoadingFun(config, requestingNum) | Function | 隐藏动画,config为该请求的config,requestingNum为目前正在请求几个接口,此时应该为0,此处可执行关闭全局动画的操作 | 否 | |
245
+ | openRefresh | Boolean | 是否开启无感知刷新token | | false |
246
+ | refreshApiUrl | String | 请求刷新token接口的api地址 如/xiaobu-admin/refresh-token | 当openRefresh为true时必传 | 无 |
247
+ | getRefreshTokenFun() | Function | 获取当前项目的 refreshToken方法, 记得 return 回去 | 当openRefresh为true时必传 | 无 |
248
+ | getAccessTokenFun() | Function | 获取当前项目的 accessToken方法, 记得 return 回去 | 当openRefresh为true时必传 | 无 |
249
+ | reloginFun(response) | Function | response 为axios实例中返回的数据,此处建议执行清除token相关缓存并弹框提示,点击确认进行刷新页面操作 | 当openRefresh为true时必传 | |
250
+ | refreshTokenStore(refreshToken) | Function | refreshToken为当前浏览器缓存的refreshToken,需要返回一个Promise,利用此refreshToken调用接口获取新的accessToken 并设置。v1.0.6及以上无需返回任何数据 | 当openRefreshtrue时必传 | |
251
+ | setAccessTokenFun(config, accessToken) (v1.0.8已废除, V2.0.2 弄回来 并添加默认值) | Function | config为该请求的config,accessToken 为新获取的accessToken, 此处需要将accessToken设置到请求头中 | 当openRefresh为true时必传 | setAccessTokenFun: (config, accessToken) => { // 设置接口请求token的 并返回新的config 默认为 config.headers.Authorization = "Bearer " + accessToken config.headers.Authorization = "Bearer " + accessToken return config }, |
252
+ | responseResultFun(res) | Function | res为axios实例返回的res,格式化接口请求成功后返回的数据 | 非必传 | axios实例返回的res |
253
+ | formatAccessTokenFun(data) v1.0.6已废除 | Function | data为responseResultFun报错时返回的res | 非必传 | axios实例返回的res.data.accessToken |
254
+ | accessTokenExpirationCode | number | 配置accessToken过期返回的业务状态码 | 非必传 | 401 |
255
+ | refreshTokenExpirationCode | number | 配置refreshToken过期返回的业务状态码 | 非必传 | 403 |
256
+ | responseTypesStr(v1.0.2) | String | 配置数据返回类型,实现当此类型时返回原封不动的响应数据的data数据 | 非必传 | 无 |
245
257
 
246
258
  ## store/modules/user.js代码大致如下
247
259
 
@@ -326,7 +338,7 @@ export default {
326
338
  this.loading = false
327
339
  }).catch(err => {
328
340
  // 此处需要添加 如果是取消请求 或者是 阻止请求时 不进行操作
329
- if (err.isPrevent || err?.message?.isCancel) {
341
+ if (err.name === "CanceledError") {
330
342
  return
331
343
  }
332
344
  this.loading = false
@@ -367,6 +379,14 @@ module.exports = {
367
379
 
368
380
  ## 更新日志
369
381
 
382
+ ### 2.0.2
383
+
384
+ 1. 【功能】阻止重复请求的代码逻辑变更
385
+ 2. 【功能】无感知刷新token的代码逻辑变更
386
+ 3. 【修复】配置高并发请求数量后,取消请求存在取消不了的情况
387
+ 4. 【优化】配置接口缓存后,新增可删除接口缓存的功能
388
+ 5. 【注意】options配置变化,及接口捕获异常处的逻辑判断
389
+
370
390
  ### 2.0.1
371
391
 
372
392
  1. 【功能】实现接口缓存配置 options新增配置 cacheNum 配置最大缓存数量, 接口配置处可配置 cache: true。
package/index.js CHANGED
@@ -1 +1 @@
1
- class e{instance;requestObj={};requestingNum=0;authErrorArr=[];queue=[];isRefresh=!1;isShowReLoginDialog=!1;runningCount=0;maxReqNum=4;tasks=[];cache;cacheNum=10;add(e){return new Promise(((t,s)=>{this.tasks.push({task:e,resolve:t,reject:s}),this._run()}))}_run(){for(;this.runningCount<this.maxReqNum&&this.tasks.length>0;){const{task:e,resolve:t,reject:s}=this.tasks.shift();this.runningCount++,e().then((e=>{this.runningCount--,"[object Object]"!==Object.prototype.toString.call(e)?t(e):t({...e,IS_COMPLETE:0===this.runningCount})})).catch((e=>{this.runningCount--,s(e)})).finally((()=>{this._run()}))}}getCache(e,t){const{url:s,data:r={},config:i,method:n}=t;return new Promise(((t,h)=>{if(!this.cache.has(e)){delete i.cache;return void(["get","delete"].includes(n)?this[n](s,i):this[n](s,r,i)).then((s=>{this.setCache(e,s),t(s)}))}const a=this.cache.get(e);this.cache.delete(e),this.cache.set(e,a),t(a)}))}setCache(e,t){if(this.cache.has(e)&&this.cache.delete(e),this.cache.set(e,t),this.cache.size>this.cacheNum){const e=this.cache.keys().next().value;this.cache.delete(e)}}get(e,t){if(t.cache){const s={url:e,config:t,method:"get"},r=t.params,i=this.generateKey(s,r);return this.getCache(i,s)}const s=t.params;return delete t.params,this.add((()=>this.instance.request({url:e,params:s,...t,method:"GET"})))}post(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"post"},i=this.generateKey(r,t);return this.getCache(i,r)}return this.add((()=>this.instance.request({url:e,data:t,...s,method:"POST"})))}delete(e,t={}){if(t.cache){const s={url:e,config:t,method:"delete"},r=t.params||t.data||{},i=this.generateKey(s,r);return this.getCache(i,s)}if(t.params){const s=t?.params||{};return delete t.params,this.add((()=>this.instance.request({url:e,params:s,...t,method:"DELETE"})))}const s=t?.data||{};return delete t.data,this.add((()=>this.instance.request({url:e,data:s,...t,method:"DELETE"})))}patch(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"patch"},i=this.generateKey(r,t);return this.getCache(i,r)}return this.add((()=>this.instance.request({url:e,data:t,...s,method:"PATCH"})))}put(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"put"},i=this.generateKey(r,t);return this.getCache(i,r)}return this.add((()=>this.instance.request({url:e,data:t,...s,method:"PUT"})))}generateKey(e,t){const{method:s,url:r}=e;let i=s+"#"+r;const n=[];for(const[e,s]of Object.entries(t))n.push(`${e}=${s}`);return n.length>0&&(i+="?"+n.join("&")),i}constructor(e,t,s){this.maxReqNum=s?.maxReqNum||this.maxReqNum,this.cacheNum=s?.cacheNum||this.cacheNum,this.cache=new Map,this.instance=t,this.instance.interceptors.request.use((t=>{t?.noShowLoading||(this.requestingNum++,s.showLoadingFun&&s.showLoadingFun(t,this.requestingNum));const r=t.url;if(s.openRefresh){-1===this.authErrorArr.indexOf(r)&&this.isRefresh&&r!==s.refreshApiUrl&&this.authErrorArr.push(r)}if(r&&t.preventDuplicateRequestsType)if("cancel"===t.preventDuplicateRequestsType){this.requestObj[r]&&this.requestObj[r].cancel({config:t,isCancel:!0});const s=e.CancelToken;this.requestObj[r]=s.source(),t.cancelToken=this.requestObj[r].token}else if("prevent"===t.preventDuplicateRequestsType){if("requesting"===this.requestObj[r])return Promise.reject({config:t,isPrevent:!0});this.requestObj[r]="requesting"}return t}),(e=>Promise.reject(e))),this.instance.interceptors.response.use((async e=>{const{config:t,responseErr:r}=e;if(t?.noShowLoading||(this.requestingNum--,this.requestingNum<=0&&(this.requestObj={},s.hideLoadingFun&&s.hideLoadingFun(t,this.requestingNum))),t&&this.requestObj[t.url]&&delete this.requestObj[t.url],r){if(s.openRefresh){const r=this.authErrorArr.indexOf(t.url);if(-1!==r)return this.authErrorArr.splice(r,1),new Promise((e=>{this.queue.push((()=>{e(this.instance.request(t))}))}));const i=s.accessTokenExpirationCode||401;if(Number(e.code)===i){const r=s.getRefreshTokenFun();if(!r){if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}if(this.isRefresh)return new Promise((e=>{this.queue.push((()=>{e(this.instance.request(t))}))}));this.isRefresh=!0;try{return await s.refreshTokenStore(r),this.queue.forEach((e=>e())),this.instance.request(t)}catch{if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}finally{this.queue=[],this.isRefresh=!1}}const n=s.refreshTokenExpirationCode||403;if(Number(e.code)===n){if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}}return Promise.reject(new Error(e.message||"Error"))}if(s?.responseTypesStr?.toLocaleLowerCase().split(",").includes(t?.responseType?.toLocaleLowerCase()))return e.data;const i=JSON.parse(JSON.stringify(e));return delete i.responseErr,delete i.config,s.responseResultFun?s.responseResultFun(i):i}),(e=>{const t=e.config||e?.message?.config||{};return t?.noShowLoading||(this.requestingNum--,this.requestingNum<=0&&(this.requestObj={},s.hideLoadingFun&&s.hideLoadingFun(t,this.requestingNum))),e.isPrevent||e?.message?.isCancel||delete this.requestObj[t.url],Promise.reject(e)}))}}export default e;
1
+ class e{instance;requestObj={};requestingNum=0;queue=[];isRefresh=!1;isShowReLoginDialog=!1;runningCount=0;maxReqNum=4;tasks=[];cache;cacheNum=10;status="start";runingReqKeys=[];add(e,t,s){return new Promise(((r,i)=>{try{if("pause"===this.status||"prevent"===s?.preventDuplicateRequestsType){if(this.tasks.some((e=>e.requestKey===t))||this.runingReqKeys.includes(t))return}if("cancel"===s?.preventDuplicateRequestsType){if(this.tasks.some((e=>e.requestKey===t))&&(this.tasks=this.tasks.filter((e=>e.requestKey!==t))),this.runingReqKeys.includes(t))return this.maxReqNum++,this.runingReqKeys.push(t),this.tasks.unshift({isCancel:!0,task:e,requestKey:t,resolve:r,reject:i}),void this._run()}this.runingReqKeys.push(t),this.tasks.push({task:e,requestKey:t,resolve:r,reject:i}),this._run()}catch(e){i(e)}}))}_pause(){this.status="pause"}_start(){this.status="start",this._run()}_run(){if("pause"!==this.status)for(;this.runningCount<this.maxReqNum&&this.tasks.length>0;){const{isCancel:e,task:t,requestKey:s,resolve:r,reject:i}=this.tasks.shift();e&&this.maxReqNum--,this.runningCount++,t().then((e=>{this.runningCount--,"[object Object]"!==Object.prototype.toString.call(e)?r(e):r({...e,IS_COMPLETE:0===this.runningCount})})).catch((e=>{this.runningCount--,i(e)})).finally((()=>{this.runingReqKeys.splice(this.runingReqKeys.indexOf(s),1),this._run()}))}}getCache(e,t){const{url:s,data:r={},config:i,method:n}=t;return new Promise(((t,h)=>{if(!this.cache.has(e)){delete i.cache;return void(["get","delete"].includes(n)?this[n](s,i):this[n](s,r,i)).then((s=>{this.setCache(e,s),t(s)}))}const a=this.cache.get(e);this.cache.delete(e),this.cache.set(e,a),t(a)}))}setCache(e,t){if(this.cache.has(e)&&this.cache.delete(e),this.cache.set(e,t),this.cache.size>this.cacheNum){const e=this.cache.keys().next().value;this.cache.delete(e)}}removeCache(){this.cache&&this.cache.clear()}get(e,t){if(t.cache){const s={url:e,config:t,method:"get"},r=t.params,i=this.generateKey(s,r);return this.getCache(i,s)}const s=t.params;if(delete t.params,t.isRefreshToken)return this.instance.request({url:e,params:s,...t,method:"GET"});const r=t.fullPath?this.generateKey({url:e,method:"get"},t.params):e;return this.add((()=>this.instance.request({url:e,params:s,...t,method:"GET"})),r,t)}post(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"post"},i=this.generateKey(r,t);return this.getCache(i,r)}if(s.isRefreshToken)return this.instance.request({url:e,data:t,...s,method:"POST"});const r=s.fullPath?this.generateKey({url:e,method:"post"},t):e;return this.add((()=>this.instance.request({url:e,data:t,...s,method:"POST"})),r,s)}delete(e,t={}){if(t.cache){const s={url:e,config:t,method:"delete"},r=t.params||t.data||{},i=this.generateKey(s,r);return this.getCache(i,s)}if(t.params){const s=t?.params||{};if(delete t.params,t.isRefreshToken)return this.instance.request({url:e,params:s,...t,method:"DELETE"});const r=t.fullPath?this.generateKey({url:e,method:"delete"},t.params):e;return this.add((()=>this.instance.request({url:e,params:s,...t,method:"DELETE"},r,t)))}const s=t?.data||{};if(delete t.data,t.isRefreshToken)return this.instance.request({url:e,data:s,...t,method:"DELETE"});const r=t.fullPath?this.generateKey({url:e,method:"delete"},s):e;return this.add((()=>this.instance.request({url:e,data:s,...t,method:"DELETE"},r,t)))}patch(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"patch"},i=this.generateKey(r,t);return this.getCache(i,r)}if(s.isRefreshToken)return this.instance.request({url:e,data:t,...s,method:"PATCH"});const r=s.fullPath?this.generateKey({url:e,method:"patch"},t):e;return this.add((()=>this.instance.request({url:e,data:t,...s,method:"PATCH"},r,s)))}put(e,t={},s={}){if(s.cache){const r={url:e,data:t,config:s,method:"put"},i=this.generateKey(r,t);return this.getCache(i,r)}if(s.isRefreshToken)return this.instance.request({url:e,data:t,...s,method:"PUT"});const r=s.fullPath?this.generateKey({url:e,method:"put"},t):e;return this.add((()=>this.instance.request({url:e,data:t,...s,method:"PUT"},r,s)))}generateKey(e,t){const{method:s,url:r}=e;let i=s+"#"+r;const n=[];for(const[e,s]of Object.entries(t))n.push(`${e}=${s}`);return n.length>0&&(i+="?"+n.join("&")),i}generateKeyByConfig(e){if(e.isRefreshToken||!e.fullPath)return e.url;const{url:t,method:s,data:r,params:i}=e;let n={};try{r&&Object.assign(n,JSON.parse(r))}catch{n={}}i&&Object.assign(n,i);const h={url:t,method:s};return this.generateKey(h,n)}constructor(e,t,s){const r=s.code||"code",i=s.accessToken||"accessToken",n=s.setAccessTokenFun||function(e,t){return e.headers.Authorization="Bearer "+t,e};this.maxReqNum=s?.maxReqNum||this.maxReqNum,this.cacheNum=s?.cacheNum||this.cacheNum,this.cache=new Map,this.instance=t,this.instance.interceptors.request.use((t=>{const r=this.generateKeyByConfig(t);t?.noShowLoading||(this.requestingNum++,s.showLoadingFun&&s.showLoadingFun(t,this.requestingNum));if(t.url&&t.preventDuplicateRequestsType&&"cancel"===t.preventDuplicateRequestsType){this.requestObj[r]&&this.requestObj[r].cancel({config:t});const s=e.CancelToken;this.requestObj[r]=s.source(),t.cancelToken=this.requestObj[r].token}return t}),(e=>Promise.reject(e))),this.instance.interceptors.response.use((async e=>{const{config:t,responseErr:h}=e,a=this.generateKeyByConfig(t);if(t?.noShowLoading||(this.requestingNum--,this.requestingNum<=0&&(this.requestObj={},s.hideLoadingFun&&s.hideLoadingFun(t,this.requestingNum))),t&&this.requestObj[a]&&delete this.requestObj[a],h){if(s.openRefresh){const h=s.accessTokenExpirationCode||401;if(Number(e[r])===h){this._pause();const r=s.getRefreshTokenFun();if(!r){if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,this.removeCache(),s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}if(this.isRefresh)return new Promise((e=>{this.queue.push((()=>{e(this.instance.request(t))}))}));this.isRefresh=!0;try{const e=t.headers[i],h=s.getAccessTokenFun();return h!==e?(this._start(),this.instance.request(n(t,h))):(await s.refreshTokenStore(r),this._start(),this.queue.forEach((e=>e())),this.instance.request(t))}catch{if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,this.removeCache(),s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}finally{this.queue=[],this.isRefresh=!1}}const a=s.refreshTokenExpirationCode||403;if(Number(e[r])===a){if(this.isShowReLoginDialog)return;return this.isShowReLoginDialog=!0,this.removeCache(),s.reloginFun(e),Promise.reject(new Error(e.message||"Error"))}}return Promise.reject(new Error(e.message||"Error"))}if(s?.responseTypesStr?.toLocaleLowerCase().split(",").includes(t?.responseType?.toLocaleLowerCase()))return e.data;const u=JSON.parse(JSON.stringify(e));return t.cache||delete u.responseErr,delete u.config,s.responseResultFun?s.responseResultFun(u):u}),(e=>{const t=e?.message?.config||{},r=this.generateKeyByConfig(t);return t?.noShowLoading||(this.requestingNum--,this.requestingNum<=0&&(this.requestObj={},s.hideLoadingFun&&s.hideLoadingFun(t,this.requestingNum))),"CanceledError"!==e?.name&&delete this.requestObj[r],Promise.reject(e)}))}}export default e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-axios-optimize",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "vue项目对axios请求的优化,实现可配置展示全局加载动画,可配置是否重复请求时取消前面的请求或者阻止后面的请求,已经控制请求并发数量,接口缓存",
5
5
  "main": "index.js",
6
6
  "scripts": {