新建wxRequest.js文件
创建WxRequest类:通过类的方式来进行封装,会让代码更加具备复用性,也可以方便添加新的属性和方法
主要内容:
- 1、封装request请求方法,成功与失败的回调;
- 2、设置请求参数
- 2.1 默认参数:在WxRequest类中添加defaults 实例属性设置默认值
- 2.2 实例化时参数:实例化时传入参数,需要在constructor构造函数形参进行接收
- 2.3 调用实例方法时传入
- 3、在request的基础上 封装请求快捷方法 get、delete、post、put实例方法;
- 4、定义请求拦截器/响应拦截器 统一处理成功和失败;
- 5、增加权限校验,再拦截器中判断是否有token访问令牌,添加到请求头;
- 6、添加并发请求Promise.all;
- 7、添加loading效果,注意并发场景loading效果 通过队列解;
- 7.1 在类型新增queue实例属性,初始值为空数组;
- 7.2 发请求前判断queue为空,则显示loading,然后立即向queue新增请求标识;
- 7.3 在complete回调中每次请求结束,从queue中移除一个请求标识,queue为空时,隐藏loading;
- 7.4 为了解决网络请求过快loading产生闪烁问题,可以使用定时器来做判断;
- 7.5 控制接口是否需要显示loading,实现可配置。showLoading。
- 8、封装uploadFile实例方法
class WxRequest {
// 实例属性定义在构造函数上方
// 定义实例属性,用来设置默认参数对象
defaults = {
baseURL: '', // 请求基准地址
url: '', // 开发者服务器接口地址
data: null, // 请求参数
method: 'GET', // 默认请求方法
// 设置请求头
header: {
'Content-type': 'application/json', // 设置数据交互格式
},
timeout: 60000, // 小程序默认超时时间 60000 一分钟
showLoading: true, // 控制是否使用默认的loading
};
// 定义拦截器对象
// 在构造函数上方 定义interceptors实例属性,属性包含request、以及response方法
interceptors = {
// 默认的拦截器 会被实例配置的拦截器覆盖
// 请求拦截器 在 请求发送之前(request前面) 对请求参数进行新增或修稿
request: (config) => config,
// 响应拦截器 在 服务器响应以后,对服务器响应的数据进行逻辑处理
response: (response) => response
};
// 定义数组队列,初始值是一个空数组,用来存储请求标识
queue = [];
/**
* 定义contructor构造函数,用于创建和初始化类的属性及方法
* @param 用户传入的请求配置项 默认值{}
*/
constructor(params = {}) {
// params:实例化时传入的参数能够被constructor进行接收
// 使用Object.assign 合并默认参数以及传递请求参数
// 注意:需要传入的参数,副覆盖默认的参数,则传入的参数要放在最后
this.defaults = Object.assign({}, this.defaults, params)
};
// request实例方法接收一个对象类型的参数options
// options属性值要和wx.request方法调用时传递的参数保持一致
request(options) {
// 如果有新的请求进来就清除上一次的定时器
this.timeId && clearTimeout(this.timeId)
// 拼接完整的url
options.url = this.defaults.baseURL + options.url;
// 合并请求参数
options = {
...this.defaults,
...options
};
if (options.showLoading && options.method !== 'UPLOAD') {
// 在请求发送之前添加loading效果 wx.showLoading()
// 判断queue队列是否为空 为空就调用wx.showloading(),不为空就不再调用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.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回调函数
// 使用wx.request请求的时候,只要接收到服务器返回的结果,无论statusCode、状态码是多少,都会走success
// 404、401等都走success,开发时需要根据业务逻辑判断
success: (res) => {
// 不论成功还是失败都调用响应拦截器 拦截器接收服务器响应的数据
// 对数据进行逻辑处理 处理后再返回,通过resolve抛出去
// 给响应拦截器传递参数时,最好把请求参数一并传递,方便代码调试或其他逻辑处理
// 需要先合并参数,然后将合并的参数传递给响应拦截器
// 可以追加一个isSuccess字段表示是否请求成功,因为无论success还是fail,都会被响应拦截器拦截
const mergeRes = Object.assign({}, res, {
config: options,
isSuccess: true
})
resolve(this.interceptors.response(mergeRes));
},
// 当接口调用失败时会触发fail回调函数
// 一般只有在 网络出现异常,请求超时等时候才会走fail
fail: (err) => {
const mergeRes = Object.assign({}, err, {
config: options,
isSuccess: false
})
reject(this.interceptors.response(mergeRes));
},
// 请求成功失败都会执行
complete: () => {
if (options.showLoading) {
// 无论成功失败,都要隐藏loading,在请求结束后,都需要从queue中删除一个标识
this.queue.pop();
this.queue.length === 0 && this.queue.push('request');
this.timeId = setTimeout(() => {
//定时器执行的时说明没有执行request一开始的clearTimeout,
// 也就是说,此时没有新的请求进来了,就可以把最后一个request标识删除
this.queue.pop();
// 删除一个标识后需要判断queue是否为空,如果为空,说明并发请求都完成了
// 就需要隐藏loading 要调用wx.showLoading
this.queue.length === 0 && wx.hideLoading();
clearTimeout(this.timeId)
}, 100)
}
}
})
}
})
}
/**
* 封装 get 请求方法
* @param { string } url 接口地址
* @param {{}} [data={}] 请求参数
* @param {{}} [config={}] 请求配置
*/
get(url, data = {}, config = {}) {
// 调用request发送请求,组织参数传递给request 将request方法的返回值return出去
return this.request(Object.assign({
url,
data,
method: 'GET'
}, config))
}
post(url, data = {}, config = {}) {
return this.request(Object.assign({
url,
data,
method: 'POST'
}, config))
}
delete(url, data = {}, config = {}) {
return this.request(Object.assign({
url,
data,
method: 'DELETE'
}, config))
}
put(url, data = {}, config = {}) {
return this.request(Object.assign({
url,
data,
method: 'PUT'
}, config))
}
/**
* 处理并发请求
* @param {...*} promise promise数组
* const resList = await instance.all(request1(),request2(),request2())
*/
all(...promise) {
// 使用展开运算符接收传递的参数,会将传入的参数转成数组
return Promise.all(parmise)
}
/**
* 文件上传接口封装
* @param { string } url 文件上传地址
* @param {string } filePath 要上传的文件资源路径
* @param {string } name 文件对应的key
* @param {{}} [config={}] 其他配置项
*/
upload(url, filePath, name = 'file', config = {}) {
return this.request(
Object.assign({
url,
filePath,
name,
method: 'UPLOAD'
}, config)
)
}
}
export default WxRequest;
新建http.js文件
对 WxRequest进行 实例化
import WxRequest from './wxRequest.js'
// 对 WxRequest进行 实例化 参数在WxRequest的constructor中接收
const instance = new WxRequest({
baseURL: 'https://tea.qingnian8.com/api',
timeout: 10000,
});
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在请求发送之前做些什么 此处会覆盖实例方法中的拦截器操作
// 在发送请求之前需要先判断本地是否存在访问令牌token,如过存在就添加到header中
const token = wx.getStorageSync('token');
if (token) {
config.header['token'] = token;
}
return config;
}
instance.interceptors.response = async (response) => {
// 对服务器响应的数据做些什么
const {
isSuccess,
data
} = response;
if (!isSuccess) {
// 请求走的是fail
wx.showToast({
title: '网络异常请重试',
icon: 'error'
})
return response;
}
// 响应成功 走的是success 直接返回响应的data
// 需要根据后端返回的业务状态码判断处理 如:200 404 401等
switch (data.code) {
case 200:
return data;
break;
case 401:
const res = await wx.showModal({
title: '提示',
content: '鉴权失败,请重新登录',
success(res) {
if (res.confirm) {
// 清楚失效的token 及本地存储的全部信息
wx.clearStorage();
// 跳到登录页
wx.navigateTo({
url: '/pages/login/login'
})
return Promise.reject(response);
} else if (res.cancel) {
console.log('用户点击取消 不做操作')
}
}
})
break;
default:
wx.showToast({
title: '程序出现异常 请重试!'
})
return Promise.reject(response);
break;
}
return data;
}
// 将实例导出
export default instance;
新建页面index.js
测试封装的wxRequest请求示例
import instance from '@/utils/http.js'
const getData1 = () => {
instance.request({
url: '/test/banner',
method: 'GET'
}).then(res => {
console.log(res);
})
}
const getData2 = async () => {
const resList = await instance.all(
instance.get('/test/banner'),
instance.get('/test/newList'),
instance.get('/test/classify'),
instance.get('/test/wallList'),
)
}
const getData3 = async () => {
const res = await instance.get('/test/banner', null, {
showLoading: false
})
}