gerson
2024-08-10 ed459ba52b9d994acc87e5d45f9293069a2f4aaa
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// 创建 WxRequest 类
// 通过类的方式来进行封装,会让代码更加具有复用性
// 也可以方便添加新的属性和方法
 
class WxRequest {
  // 定义实例属性,用来设置默认请求参数
  defaults = {
    baseURL: '', // 请求基准地址
    url: '', // 接口的请求路径
    data: null, // 请求参数
    method: 'GET', // 默认的请求方法
    // 请求头
    header: {
      'Content-type': 'application/json' // 设置数据的交互格式
    },
    timeout: 60000, // 默认的超时时长,小程序默认的超时时长是 1 分钟
    isLoading: true // 控制是否使用默认的 loading,默认值是 true 表示使用默认的 loading
  }
 
  // 定义拦截器对象
  // 需要包含请求拦截器以及响应拦截器,方便在请求之前以及响应以后时进行逻辑处理
  interceptors = {
    // 请求拦截器
    // 在请求发送之前,对请求参数进行新增或者修改
    request: (config) => config,
 
    // 响应拦截器
    // 在服务器响应数据以后,对服务器响应的数据进行逻辑处理
    response: (response) => response
  }
 
  // 定义数组队列
  // 初始值需要是一个空数组,用来存储请求队列、存储请求标识
  queue = []
 
  // 用于创建和初始化类的属性以及方法
  // 在实例化时传入的参数,会被 constructor 形参进行接收
  constructor(params = {}) {
    // 通过 Object.assign 方法合并请求参数
    // 注意:需要传入的参数,覆盖默认的参数,因此传入的参数需要放到最后
    this.defaults = Object.assign({}, this.defaults, params)
  }
 
  // request 实例方法接收一个对象类型的参数
  // 属性值和 wx.request 方法调用时传递的参数保持一致
  request(options) {
    // 如果有新的请求,就清除上一次的定时器
    this.timerId && clearTimeout(this.timerId)
 
    // 注意:需要先合并完整的请求地址 (baseURL + url)
    // https://gmall-prod.atguigu.cn/mall-api/index/findBanner
    options.url = this.defaults.baseURL + options.url
 
    // 合并请求参数
    options = { ...this.defaults, ...options }
 
    // 在请求发送之前,添加 loading 效果
    // wx.showLoading()
 
    if (options.isLoading && options.method !== 'UPLOAD') {
      // 判断 queue 队列是否为空,如果是空,就显示 loading
      // 如果不是空,就不显示 loading,不调用 wx.showLoading()
      this.queue.length === 0 && wx.showLoading()
 
      // 然后立即向 queue 数组队列中添加请求标识
      // 每个标识代表是一个请求,标识是自定义的
      this.queue.push('request')
    }
 
    // 在请求发送之前,调用请求拦截器,新增和修改请求参数
    options = this.interceptors.request(options)
 
    // 需要使用 Promise 封装 wx.request,处理异步请求
    return new Promise((resolve, reject) => {
      if (options.method === 'UPLOAD') {
        wx.uploadFile({
          ...options,
 
          success: (res) => {
            // 需要将服务器返回的 JSON 字符串 通过 JSON.parse 转成对象
            res.data = JSON.parse(res.data)
 
            // 合并参数
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
 
            resolve(this.interceptors.response(mergeRes))
          },
 
          fail: (err) => {
            // 合并参数
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
 
            reject(this.interceptors.response(mergeErr))
          }
        })
      } else {
        wx.request({
          ...options,
 
          // 当接口调用成功时会触发 success 回调函数
          success: (res) => {
            // 不管是成功响应还是失败响应,都需要调用响应拦截器
            // 响应拦截器需要接收服务器响应的数据,然后对数据进行逻辑处理,处理好以后进行返回
            // 然后在通过 resolve 将返回的数据抛出去
 
            // 在给响应拦截器传递参数时,需要将请求参数也一起传递
            // 方便进行代码的调试或者进行其他逻辑处理,需要先合并参数
            // 然后将合并的参数传递给响应拦截器
 
            // 不管是请求失败还是请求成功,都已经将响应的数据传递给了响应拦截器
            // 这时候在合并参数的时候,追加一个属性:isSuccess
            // 如果属性值为 true,说明执行了 success 回调函数
            // 如果属性值为 false,说明执行了 fail 回调函数
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
            resolve(this.interceptors.response(mergeRes))
          },
 
          // 当接口调用失败时会触发 fail 回调函数
          fail: (err) => {
            // 不管是成功响应还是失败响应,都需要调用响应拦截器
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
            reject(this.interceptors.response(mergeErr))
          },
 
          // 接口调用结束的回调函数(调用成功、失败都会执行)
          complete: () => {
            if (options.isLoading) {
              // 在每一个请求结束以后,都会执行 complete 回调函数
              // 每次从 queue 队列中删除一个标识
              this.queue.pop()
 
              this.queue.length === 0 && this.queue.push('request')
 
              this.timerId = setTimeout(() => {
                this.queue.pop()
 
                // 在删除标识以后,需要判断目前 queue 数组是否为空
                // 如果是空,说明并发请求完成了
                // 就需要隐藏 loading,要调用 wx.hideLoading()
                this.queue.length === 0 && wx.hideLoading()
 
                clearTimeout(this.timerId)
              }, 1)
 
              // 不管请求时成功还是失败,都需要隐藏 loading
              // wx.hideLoading()
            }
          }
        })
      }
    })
  }
 
  // 封装 GET 实例方法
  get(url, data = {}, config = {}) {
    // 需要调用 request 请求方法发送请求,只需要组织好参数,传递给 request 请求方法即可
    // 当调用 get 方法时,需要将 request 方法的返回值 return 出去
    return this.request(Object.assign({ url, data, method: 'GET' }, config))
  }
 
  // 封装 DELETE 实例方法
  delete(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
  }
 
  // 封装 POST 实例方法
  post(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'POST' }, config))
  }
 
  // 封装 PUT 实例方法
  put(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'PUT' }, config))
  }
 
  // 用来处理并发请求
  all(...promise) {
    // 通过展开运算符接收传递的参数
    // 那么展开运算符会将传入的参数转成数组
    // console.log(promise)
 
    return Promise.all(promise)
  }
 
  /**
   * @description upload 实例方法,用来对 wx.uploadFile 进行封装
   * @param {*} url 文件的上传地址、接口地址
   * @param {*} filePath 要上传的文件资源路径
   * @param {*} name 文件对应的 key
   * @param {*} config 其他配置项
   */
  upload(url, filePath, name = 'file', config = {}) {
    return this.request(
      Object.assign({ url, filePath, name, method: 'UPLOAD' }, config)
    )
  }
}
 
export default WxRequest