一、request实例方法
js
// 创建WxRequest类
// 通过类的方式来进行封装,会让代码更加具有复用性
// 也可以方便添加新的属性和方法
class WxRequest {
// 用于创建和初始化类的属性和方法
constructor() { }
// request 实例方法接收一个对象类型的参数
// 属性值和wx.request方法调用时传递的参数保持一致
request(options) {
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
resolve(res)
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
reject(err)
}
})
})
}
}
对WxRequest进行实例化
js
// 对WxRequest进行实例化
const instance = new WxRequest()
// 将WxRequest实例暴露出去,方便在其他文件中进行使用
export default instance
按钮绑定事件handler
js
<button type="warn" plain bind:tap="handler">测试发送请求</button>
两种方法进行调用
js
async handler() {
// 第一种调用方法,.then的方式进行调用
instance.request({
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
method: "GET"
}).then((res) => {
console.log(res)
})
// 第二种调用方式,async和await的方式进行调用
const res = await instance.request({
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
method: "GET"
})
console.log(res)
},
两种方法都调用请求成功

resolve方法:- resolve
是一个函数,用于在异步操作成功完成时调用,并将操作的结果作为参数传递给 resolve
。这将使 Promise 对象变为已解决(fulfilled)状态,并传递结果给 Promise 的 .then()
方法中的回调函数。
reject方法: reject
也是一个函数,用于在异步操作失败时调用,并将错误作为参数传递给 reject
。这将使 Promise 对象变为已拒绝(rejected)状态,并传递错误给 Promise 的 .catch()
方法中的回调函数。
二、设置请求参数
请求参数的设置有三种方式:
- 默认参数:在WxRequest类中添加defaults实例属性来设置默认值
- 实例化时参数:在对WxRequest类进行实例化时传入相关的参数,需要在constructor构造函数形参进行接收
- 调用实例方法时传入请求参数
在类内部定义默认的请求参数
js
// 定义实例属性,用来设置默认请求参数
defaults = {
// 请求基地址
baseURL: '',
// 请求路径
url: '',
// 请求参数
data: null,
// 默认的请求方法
method: 'GET',
// 请求头
header: {
// 设置数据的交互格式
'Content-type': 'application/json'
},
// 默认的超时时长,小程序默认的超时时长是1分钟
timeout: 60000
}
当用户实例化时会传入相关的参数
js
// 对WxRequest进行实例化
// 现在会执行 constructor 中的代码
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
传入的参数会被constructor形参进行接收,在constructor内部需要来合并请求参数
js
// 用于创建和初始化类的属性和方法
// 在实例化时传入的参数,会被constructor形参进行接收
constructor(params = {}) {
// 从右往左 params覆盖this.defaults,再将this.defaults和{}进行合并
// 通过Object.assign方法合并请求参数 注意:需要传入的参数,覆盖默认的参数,因此传入的参数需要放到最后
this.defaults = Object.assign({}, this.defaults, params)
}
当我们在调用实例方法时,也会传入请求参数
js
// 第二种调用方式,async和await的方式进行调用
const res = await instance.request({
url: '/index/findBanner',
method: "GET"
})
传入的请求参数需要在实例化方法内部来进行合并
js
request(options) {
// 注意:需要先合并完成的请求地址 baseURL+url
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
resolve(res)
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
reject(err)
}
})
})
}
}
最后进行调用测试
js
// 第二种调用方式,async和await的方式进行调用
const res = await instance.request({
url: '/index/findBanner',
method: "GET"
})
console.log(res)
发起请求成功

三、封装GET POST DELETE PUT请求快捷方法
js
/*
封装GET实例方法
*/
get(url, data = {}, config = {}) {
// 需要调用request请求方法发送请求,只需要组织好参数,传递给request请求方法即可
// 当调用get方法时,需要将request方法的返回值return出去
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
/*
封装POST实例方法
*/
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
/*
封装DELETE实例方法
*/
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
/*
封装PUT实例方法
*/
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
传入url,data,config进行测试
js
const res = await instance.get('/index/findBanner', { test: 111 }, { timeout: 20000 })
console.log(res)
测试成功

四、网络请求封装时的注意事项
在使用wx.request发送请求时,只要成功接收到服务器返回的结果,无论statusCode状态码是多少,都会执行success。开发者需要根据业务逻辑,自己来进行相关的判断处理
js
handler1() {
wx.request({
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner888',
method: 'GET',
success: (res) => {
// 在使用wx.request发送请求时,只要成功接收到服务器返回的结果
// 无论statusCode状态码是多少,都会执行success
// 开发者需要根据业务逻辑,自己来进行相关的判断处理
console.log('虽然接口出错了,但是我依然会走success')
console.log(res)
},
fail: (err) => {
console.log(err)
}
})
},
一般在网络出现异常时(网络超时),就会执行fail
五、定义请求---响应拦截器
在WxRequest类内部定义interceptors实例属性,属性中需要包含request以及response方法 需要注意:在发送请求时,还需要区分是否通过实例调用了拦截器
- 没有通过实例调用拦截器,需要定义默认拦截器,在默认拦截器中,需要将请求参数进行返回
- 通过实例调用拦截器,那么实例调用的拦截器会覆盖默认的拦截器方法,然后将新增或修改的请求参数进行返回
定义默认拦截器
js
// 定义拦截器对象
// 需要包含请求拦截器以及响应拦截器,方便在请求之前以及相应之后进行逻辑处理
interceptors = {
// 请求拦截器
// 在请求发送之前,对请求参数进行新增或者修改
request: (config) => config,
// 响应拦截器
// 在服务器响应数据以后,对服务器响应的数据进行逻辑处理
response: (response) => response
}
通过实例配置请求-响应拦截器
js
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在请求发送之前做点什么......
return config
}
// 配置响应拦截器
instance.interceptors.response = (response) => {
// 对服务器响应数据做点什么......
return response
}
调用请求-响应拦截器
在请求发送之前,调用请求拦截器,新增和修改请求参数
js
request(options) {
// 注意:需要先合并完成的请求地址 baseURL+url
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// console.log(options)
// 在请求发送之前,调用请求拦截器,新增和修改请求参数
options = this.interceptors.request(options)
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
resolve(res)
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
reject(err)
}
})
})
}
不管是成功相响应还是失败响应,都需要调用响应拦截器,响应拦截器需要接收服务器响应的数据,然后对数据进行逻辑处理,处理好以后在返回,然后通过resolve将返回的数据抛出去。 在给响应拦截器传递参数时,需要将请求参数也一起传递,方便进行代码的调试或者进行其他逻辑处理,需要先合并参数,然后将合并的参数传递给响应拦截器。
js
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
const mergeRes = Object.assign({}, res, { config: options })
resolve(this.interceptors.response(mergeRes))
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
const mergeErr = Object.assign({}, err, { config: options })
resolve(this.interceptors.response(mergeErr))
reject(err)
}
})
实例中配置的请求拦截器和响应拦截器会覆盖默认请求响应拦截器 不管注不注释实例的拦截器,都可以发送请求成功

六、完善响应拦截器
在响应拦截器,我们需要判断请求成功还是请求失败,然后进行不同的业务逻辑处理。 但是目前不管是请求成功还是请求失败,都会执行响应拦截器。
如何判断?
如果请求成功,将响应成功的数据传递给响应拦截器,同时在传递的数据中新增isSuccess:true字段,表示请求成功
如果请求失败,将响应失败的数据传递给响应拦截器,同时在传递的数据中新增isSuccess:false字段,表示请求失败
在实例调用的响应拦截中,根据传递的数据进行了以下的处理:
如果isSuccess:true 表示服务器响应了结果,我们可以将服务器响应的数据简化以后进行返回
如果isSuccess:false 表示网络超时或其他网络问题,提示 网络异常,同时返回即可
js
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
resolve(this.interceptors.response(mergeRes))
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
const mergeErr = Object.assign({}, err, { config: options, isSuccess: false })
resolve(this.interceptors.response(mergeErr))
reject(err)
}
})
在响应拦截器中打印一下response,看一下返回的数据中有没有isSuccess
js
// 配置响应拦截器
instance.interceptors.response = (response) => {
// 对服务器响应数据做点什么......
console.log(response)
return response
}
测试发现可以打印
测试一下失败的情况 把timeout改成100
js
// 现在会执行 constructor 中的代码
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 100
})
将网络改成3G 测试发现isSuccess的值为false

针对isSuccess的不同情况,对response的返回结果进行不同处理,当请求成功时,我们真正需要的数据就是response.data

js
// 配置响应拦截器
instance.interceptors.response = (response) => {
// 从response中解构isSuccess
const { isSuccess,data } = response
// 如果isSuccess为false,说明执行了fail回调函数
// 这时候就说明网络异常,需要给用户提示网络异常
if (!isSuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
// 对服务器响应数据做点什么......
// console.log(response)
// 如果网络请求执行成功到这里,那么我们直接把data进行返回即可
// return response
return data
}

七、完善请求、响应拦截器
在发送请求时,购物车列表、收货地址、更新头像等接口,都需要进行权限验证,因此我们需要在请求拦截器中判断本地是否存在访问令牌token,如果存在就需要在请求头中添加token字段
在使用wx.request发送网络请求时,只要成功接收到服务器返回,无论statusCode是多少,都会进入success回调,因此开发者根据业务逻辑对返回值及逆行判断。
后端返回的业务状态码如下:
- 业务状态码 === 200,说明接口已请求成功,服务器成功返回了数据
- 业务状态码 === 208,说明没有token或者token过期失效,需要登录或者重新登陆
- 业务状态码 === 其他,说明请求或者响应出现了异常
js
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在请求发送之前做点什么......
// 在发送请求之前,需要先判断本地是否存在访问令牌 token
const token = getStorage('token')
// 如果存在token,就需要在请求头中添加token字段
if (token) {
// 这里的['token']是一个属性访问器的语法,它允许你使用字符串作为键(key)来访问或设置对象的属性。
config.header['token'] = token
}
return config
}
在storage中添加token字段,再发请求测试header中是否加上了token
测试成功

在响应拦截器中判断服务器的响应状态码
js
// 配置响应拦截器
instance.interceptors.response = async (response) => {
// 从response中解构isSuccess
const { isSuccess, data } = response
// 如果isSuccess为false,说明执行了fail回调函数
// 这时候就说明网络异常,需要给用户提示网络异常
if (!isSuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
// 对服务器响应数据做点什么......
// console.log(response)
// 判断服务器响应的业务状态码
switch (data.code) {
// 如果后端返回的业务状态码等于200,说明请求成功,服务器成功响应了数据
case 200:
// 对服务器响应数据做点什么......
return data
// 如果返回的业务状态码等于208,说明没有token,或者token失效
// 就需要让用户登录或者重新登陆
case 208:
const res = await modal({
content: "鉴权失败,请重新登录",
showCancel: false // 不显示取消按钮
})
if (res) {
// 消除之前失效的token,同时需要清除本地存储的全部信息
clearStorage()
wx.navigateTo({
url: '/modules/otherModule/pages/login/login.js',
})
}
return Promise.reject(response)
default:
toast({
title: "程序出现异常,请联系客服或稍后重试"
})
return Promise.reject(response)
}
// 如果网络请求执行成功到这里,那么我们直接把data进行返回即可
// return response
return data
}
测试208
在storage中写入一个假的token,运行项目

并且storage中的token被清除
js
const res = await instance.get('/cart/getCartList').catch((err)=>{
console.log(err)
})
捕获错误 返回的状态码是208
故意写错url
js
const res = await instance.get('/cart/getCartList2222')
console.log(res)

八、发起并发请求
前端并发请求是指在前端页面同时向后端发起多个请求的情况。当一个页面需要请求多个接口获取数据时,为了提高页面的加载速度和用户体验,可以同时发起多个请求,这些请求之间就是并发的关系。
我们通过两种方式演示发起多个请求
- 使用async和await
- 使用promise.all
js
async allHandler() {
// 演示通过async和await方式同时发起多个请求
// async和await能够控制异步任务以同步的流程来执行
await instance.get('/index/findBanner')
await instance.get('/index/findCategory1')
await instance.get('/index/findBanner')
await instance.get('/index/findCategory1')
},
进度条以台阶的方式进行排列,说明当前的四个请求是按照同步流程来进行发送的 四个请求的时间之和加起来的总时间之后,数据才会进行渲染
以async和await方式发起请求,当第一个请求结束以后,才能发起第二个请求。在前一个请求结束以后,才能够发起下一个请求。会造成请求的阻塞,从而影响页面的渲染速度
js
async allHandler() {
// 演示通过Promise.all同时发起多个请求
// Promise.all 能够将多个请求同时进行发送
await Promise.all([instance.get('/index/findBanner'),instance.get('/index/findCategory1'),instance.get('/index/findBanner'),instance.get('/index/findCategory1')])
},
Promise.all能够将多个异步请求同时进行发送,也就是并行发送,并不会造成请求的阻塞,从而不会影响页面的渲染速度
封装all方法用来处理并发请求
js
/*
封装all方法用来处理并发请求
*/
all(...promise) {
console.log(promise)
// 通过展开运算符接受传递的参数
// 那么展开运算符会将传入的参数转成数组
return Promise.all(promise)
}
调用all方法
js
const res = await instance.all(instance.get('/index/findBanner'),instance.get('/index/findCategory1'),instance.get('/index/findBanner'),instance.get('/index/findCategory1'))
console.log(res)


九、添加loading效果
在封装时添加loading效果,从而提高用户体验
- 在请求发送之前,需要通过wx.showLoading展示loading效果
- 当服务器响应数据以后,需要调用wx.hideLoading隐藏loading效果
js
request(options) {
// 注意:需要先合并完成的请求地址 baseURL+url
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 在请求发送之前,添加loading效果
wx.showLoading()
// console.log(options)
// 在请求发送之前,调用请求拦截器,新增和修改请求参数
options = this.interceptors.request(options)
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
resolve(this.interceptors.response(mergeRes))
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
const mergeErr = Object.assign({}, err, { config: options, isSuccess: false })
resolve(this.interceptors.response(mergeErr))
reject(err)
},
// 接口调用结束的回调函数(调用成功 失败都会执行)
complete: () => {
// 不管请求是成功还是失败,都需要隐藏loading
wx.hideLoading()
}
})
})
}
十、完善loading
目前在发送请求时,请求发送之前会展示loading,相应之后会隐藏loading,但是loading的展示和隐藏会存在以下问题:
- 每次请求都会执行wx.showLoading(),但是页面中只会显示一个,后面的loading会将前面的覆盖
- 同时发起多次请求,只要有一个请求成功响应就会调用wx.hideLoading,导致其他请求还没完成,也不会loading
- 请求过快或一个请求在另一个请求后立即触发,这时候会出现loading闪烁问题
我们通过队列的方式解决这三个问题:首先在类中新增一个实例属性queue,初始只是一个空数组
- 发起请求之前,判断queue如果是空数组则显示loading,然后立即向queue新增请求标识
- 在complete中每次请求成功结束,从queue中移除一个请求标识,queue为空时隐藏loading
- 为了解决网络请求过快产生loading闪烁问题,可以使用定时器来做判断即可。
定义队列数组
js
// 定义数组队列
// 初始值需要是一个空数组,用来存储请求队列\存储请求标识
queue = []
js
// request 实例方法接收一个对象类型的参数
// 属性值和wx.request方法调用时传递的参数保持一致
request(options) {
// 如果有新的请求,就清除上一次的定时器
this.timeId && clearTimeout(this.timeId)
// 注意:需要先合并完成的请求地址 baseURL+url
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 在请求发送之前,添加loading效果
// wx.showLoading()
// 判断queue队列是否为空,如果是空,就显示loading
// 如果不是空,就不显示loading,不调用wx.showLoading()
this.queue.length === 0 && wx.showLoading()
// 然后立即向queue数组队列中添加请求标识
// 每个标识代表是一个请求,标识是自定义的
this.queue.push('request')
// console.log(options)
// 在请求发送之前,调用请求拦截器,新增和修改请求参数
options = this.interceptors.request(options)
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
resolve(this.interceptors.response(mergeRes))
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
const mergeErr = Object.assign({}, err, { config: options, isSuccess: false })
resolve(this.interceptors.response(mergeErr))
reject(err)
},
// 接口调用结束的回调函数(调用成功 失败都会执行)
complete: () => {
// 不管请求是成功还是失败,都需要隐藏loading
// wx.hideLoading()
// 在每一个请求结束以后,都会执行complete回调函数
// 每次从queue队列中删除一个标识
this.queue.pop()
this.queue.length === 0 && this.queue.push('request')
this.timeId = setTimeout(() => {
this.queue.pop()
// 在删除标识之后,需要判断目前queue数组是否为空
// 如果是空,说明并发请求完成了
// 就需要隐藏loading,要调用wx.hideLoading()
this.queue.length === 0 && wx.hideLoading()
clearTimeout(thie.timeId)
}, 1);
}
})
})
}
十一、控制loading的显示
在我们封装的网络请求文件中,通过wx.showLoading默认显示了loading效果
但在实际开发中,有的接口可能不需要显示loading效果,或者开发者希望自己来控制loading的样式与交互,那么就需要关闭默认loading效果。
这时我们就需要一个开关来控制loading显示
- 类内部设置默认请求cabs关于isLoading属性,默认值是true,在类内部根据isLoading属性做判断即可
- 某个接口不需要显示loading效果,可以在发送请求的时候,可以新增请求配置isLoading设置为false
- 整个项目都不需要显示loading效果,可以在实例化的时候,传入isLoading配置为false
增加Loading属性
js
// 定义实例属性,用来设置默认请求参数
defaults = {
// 请求基地址
baseURL: '',
// 请求路径
url: '',
// 请求参数
data: null,
// 默认的请求方法
method: 'GET',
// 请求头
header: {
// 设置数据的交互格式
'Content-type': 'application/json'
},
// 默认的超时时长,小程序默认的超时时长是1分钟
timeout: 60000,
// 控制是否适用默认的loading,默认值是true 表示使用默认的loading
isLoading: true
}
js
if (options.isLoading) {
// 判断queue队列是否为空,如果是空,就显示loading
// 如果不是空,就不显示loading,不调用wx.showLoading()
this.queue.length === 0 && wx.showLoading()
// 然后立即向queue数组队列中添加请求标识
// 每个标识代表是一个请求,标识是自定义的
this.queue.push('request')
}
js
// 接口调用结束的回调函数(调用成功 失败都会执行)
complete: () => {
if (options.isLoading) {
// 不管请求是成功还是失败,都需要隐藏loading
// wx.hideLoading()
// 在每一个请求结束以后,都会执行complete回调函数
// 每次从queue队列中删除一个标识
this.queue.pop()
this.queue.length === 0 && this.queue.push('request')
this.timeId = setTimeout(() => {
this.queue.pop()
// 在删除标识之后,需要判断目前queue数组是否为空
// 如果是空,说明并发请求完成了
// 就需要隐藏loading,要调用wx.hideLoading()
this.queue.length === 0 && wx.hideLoading()
clearTimeout(thie.timeId)
}, 1);
}
}
设置某个接口不要Loading效果
js
const res = await instance.get('/index/findBanner',null,{
isLoading:false
})
console.log(res)
整个项目中都不想使用默认的Loading
js
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000,
isLoading: false
})
如果整个项目都不使用Loading但是在某个接口要使用Loading
js
const res = await instance.get('/index/findBanner',null,{
isLoading:true
})
console.log(res)
十二、接口调用
js
// 导入封装的网络请求模块实例
import http from '../utils/http'
export const reqSwiperData = () => {
return http.get('/index/findBanner')
}
js
import {reqSwiperData} from '../../api/index'
async handler() {
const res = await reqSwiperData()
console.log(res)
},
十三、完整代码
request.js
js
// 创建WxRequest类
// 通过类的方式来进行封装,会让代码更加具有复用性
// 也可以方便添加新的属性和方法
class WxRequest {
// 定义实例属性,用来设置默认请求参数
defaults = {
// 请求基地址
baseURL: '',
// 请求路径
url: '',
// 请求参数
data: null,
// 默认的请求方法
method: 'GET',
// 请求头
header: {
// 设置数据的交互格式
'Content-type': 'application/json'
},
// 默认的超时时长,小程序默认的超时时长是1分钟
timeout: 60000,
// 控制是否适用默认的loading,默认值是true 表示使用默认的loading
isLoading: true
}
// 定义拦截器对象
// 需要包含请求拦截器以及响应拦截器,方便在请求之前以及相应之后进行逻辑处理
interceptors = {
// 请求拦截器
// 在请求发送之前,对请求参数进行新增或者修改
request: (config) => config,
// 响应拦截器
// 在服务器响应数据以后,对服务器响应的数据进行逻辑处理
response: (response) => response
}
// 定义数组队列
// 初始值需要是一个空数组,用来存储请求队列\存储请求标识
queue = []
// 用于创建和初始化类的属性和方法
// 在实例化时传入的参数,会被constructor形参进行接收
constructor(params = {}) {
// 从右往左 params覆盖this.defaults,再将this.defaults和{}进行合并
// 通过Object.assign方法合并请求参数 注意:需要传入的参数,覆盖默认的参数,因此传入的参数需要放到最后
this.defaults = Object.assign({}, this.defaults, params)
}
// request 实例方法接收一个对象类型的参数
// 属性值和wx.request方法调用时传递的参数保持一致
request(options) {
// 如果有新的请求,就清除上一次的定时器
this.timeId && clearTimeout(this.timeId)
// 注意:需要先合并完成的请求地址 baseURL+url
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 在请求发送之前,添加loading效果
// wx.showLoading()
if (options.isLoading) {
// 判断queue队列是否为空,如果是空,就显示loading
// 如果不是空,就不显示loading,不调用wx.showLoading()
this.queue.length === 0 && wx.showLoading()
// 然后立即向queue数组队列中添加请求标识
// 每个标识代表是一个请求,标识是自定义的
this.queue.push('request')
}
// console.log(options)
// 在请求发送之前,调用请求拦截器,新增和修改请求参数
options = this.interceptors.request(options)
// 需要使用Promise封装wx.request处理异步请求
return new Promise((resolve, reject) => {
wx.request({
// ...options 是 ES6 的扩展运算符,它将 options 对象的所有属性展开并传递给 wx.request
...options,
// 当接口调用成功时会触发success回调函数, 使用 resolve 返回成功的结果
success: (res) => {
const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
resolve(this.interceptors.response(mergeRes))
},
// 当接口调用失败时会触发fail回调函数, 使用 reject 返回错误信息。
fail: (err) => {
const mergeErr = Object.assign({}, err, { config: options, isSuccess: false })
resolve(this.interceptors.response(mergeErr))
reject(err)
},
// 接口调用结束的回调函数(调用成功 失败都会执行)
complete: () => {
if (options.isLoading) {
// 不管请求是成功还是失败,都需要隐藏loading
// wx.hideLoading()
// 在每一个请求结束以后,都会执行complete回调函数
// 每次从queue队列中删除一个标识
this.queue.pop()
this.queue.length === 0 && this.queue.push('request')
this.timeId = setTimeout(() => {
this.queue.pop()
// 在删除标识之后,需要判断目前queue数组是否为空
// 如果是空,说明并发请求完成了
// 就需要隐藏loading,要调用wx.hideLoading()
this.queue.length === 0 && wx.hideLoading()
clearTimeout(this.timeId)
}, 1);
}
}
})
})
}
/*
封装GET实例方法
*/
get(url, data = {}, config = {}) {
// 需要调用request请求方法发送请求,只需要组织好参数,传递给request请求方法即可
// 当调用get方法时,需要将request方法的返回值return出去
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
/*
封装POST实例方法
*/
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
/*
封装DELETE实例方法
*/
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
/*
封装PUT实例方法
*/
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
/*
封装all方法用来处理并发请求
*/
all(...promise) {
console.log(promise)
// 通过展开运算符接受传递的参数
// 那么展开运算符会将传入的参数转成数组
return Promise.all(promise)
}
}
export default WxRequest
http.js
js
import WxRequest from './request'
import { getStorage, clearStorage } from './storage'
import { modal, toast } from './extendApi'
// 对WxRequest进行实例化
// 现在会执行 constructor 中的代码
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000,
isLoading: false
})
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在请求发送之前做点什么......
// 在发送请求之前,需要先判断本地是否存在访问令牌 token
const token = getStorage('token')
// 如果存在token,就需要在请求头中添加token字段
if (token) {
// 这里的['token']是一个属性访问器的语法,它允许你使用字符串作为键(key)来访问或设置对象的属性。
config.header['token'] = token
}
return config
}
// 配置响应拦截器
instance.interceptors.response = async (response) => {
// 从response中解构isSuccess
const { isSuccess, data } = response
// 如果isSuccess为false,说明执行了fail回调函数
// 这时候就说明网络异常,需要给用户提示网络异常
if (!isSuccess) {
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response
}
// 对服务器响应数据做点什么......
// console.log(response)
// 判断服务器响应的业务状态码
switch (data.code) {
// 如果后端返回的业务状态码等于200,说明请求成功,服务器成功响应了数据
case 200:
// 对服务器响应数据做点什么......
return data
// 如果返回的业务状态码等于208,说明没有token,或者token失效
// 就需要让用户登录或者重新登陆
case 208:
const res = await modal({
content: "鉴权失败,请重新登录",
showCancel: false // 不显示取消按钮
})
if (res) {
// 消除之前失效的token,同时需要清除本地存储的全部信息
clearStorage()
wx.navigateTo({
url: '/modules/otherModule/pages/login/login',
})
}
return Promise.reject(response)
default:
toast({
title: "程序出现异常,请联系客服或稍后重试"
})
return Promise.reject(response)
}
// 如果网络请求执行成功到这里,那么我们直接把data进行返回即可
// return response
return data
}
// 将WxRequest实例暴露出去,方便在其他文件中进行使用
export default instance