微信小程序 wx.request 封装(构建-请求拦截、响应拦截)
一个优雅的前端,搬的每一块砖都必须是优雅,美观的。那我们就先从请求封装开始吧! (如果有使用 uni的小伙伴,一样可以使用,把
wx.
替换成uni.
就可以了)
请求封装的代码
function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
function forEach(obj, fn) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn(obj[key], key);
}
}
}
// 对象合并 === 如果你的工具库中已有该方法的实现,可以将其替换 ===
export function merge() {
var result = {};
function assignValue(val, key) {
if (isObject(result[key]) && isObject(val)) {
result[key] = merge(result[key], val);
} else {
result[key] = val;
}
}
for (var i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue);
}
return result;
}
// http规则校验
function urlRegExp(value) {
return /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/.test(value)
}
class Http {
constructor(config = {}) {
this.config = merge({}, {
baseUrl: '', // 请求的根域名
header: {}, // 默认的请求头
method: 'POST',
dataType: 'json',
responseType: 'text', // 此参数无需处理,因为5+和支付宝小程序不支持,默认为text即可
loading: true, // 默认请求是否开启loading
}, config)
// 拦截器
this.interceptor = {
// 请求前的拦截
request: null,
// 请求后的拦截
response: null
}
}
// 主要请求部分
request(options = {}) {
let requestTask = {}
if (options.loading === undefined) options.loading = this.config.loading
// 检查请求拦截
if (this.interceptor.request && typeof this.interceptor.request === 'function') {
let tmpConfig = {};
let interceptorRequest = this.interceptor.request(options);
if (interceptorRequest === false) {
// 返回一个处于pending状态中的Promise,来取消原promise,避免进入then()回调
return new Promise(() => {});
}
this.options = interceptorRequest;
}
options.dataType = options.dataType || this.config.dataType;
options.responseType = options.responseType || this.config.responseType;
options.url = options.url || '';
options.params = options.params || {};
options.header = merge({}, this.config.header, options.header);
options.method = options.method || this.config.method;
return new Promise((resolve, reject) => {
options.complete = (response) => {
response.loading = options.loading
// 判断是否存在拦截器
if (this.interceptor.response && typeof this.interceptor.response === 'function') {
let resInterceptors = this.interceptor.response(response);
// 如果拦截器不返回false,直接接入then回调
if (resInterceptors !== false) {
resolve(resInterceptors);
} else {
// 如果拦截器返回false,意味着拦截器定义者认为返回有问题,直接接入catch回调
reject(response.data || response);
}
} else {
// 如果要求返回原始数据,就算没有拦截器,也返回最原始的数据
resolve(response);
}
}
// 判断用户传递的URL是否/开头,如果不是,加上/,
options.url = urlRegExp(options.url) ? options.url : (this.config.baseUrl + options.url);
// 发送请求
requestTask = wx.request(options);
if (options.setCancelToken) options.setCancelToken(requestTask)
})
}
}
export default Http
使用demo:这里我们新建一个request.js文件
将刚刚封装好的http文件引入,利用请求响应拦截器做一些请求的配置
const ACCESS_TOKEN = 'AurhToken' // token凭证的key
import HTTP from './http.js'
// 创建配置信息
const Config = {
baseUrl: '', // 公共请求前缀(域名)
timeout: 10 * 1000, // 请求超时时间
loading: true // 是否启用pending状态的loading遮罩
}
// 初始化请求实例
const HttpClint = new HTTP(Config)
// 请求拦截配置项
const LoadingDelayTime = 300 // showLoading 延迟时间
let requestNum = 0 // 请求次数
let showLoading = false // loading 状态
let loadingTimer = null // showLoading 定时器
let RedirectTimer = null // 重新登录 定时器
// 请求拦截器
HttpClint.interceptor.request = config => {
// 添加loading
if (config.loading) {
requestNum++
// 请求队列中,第一个请求时,创建loading
if (requestNum === 1) {
loadingTimer = setTimeout(() => {
showLoading = true
wx.showLoading({
title: 'loading...',
mask: true
})
}, LoadingDelayTime)
}
}
// 添加 Token 凭证
if (typeof config.header !== 'object') config.header = {}
config.header[ ACCESS_TOKEN ] = 'This is a token content'
return config
}
// 响应拦截器
HttpClint.interceptor.response = response => {
// 关闭 Loading
if (response.loading) {
requestNum--
if (requestNum === 0) {
if (loadingTimer) {
clearTimeout(loadingTimer)
loadingTimer = null
}
if (showLoading) {
showLoading = false
wx.hideLoading()
}
}
}
/* 以下是一个 响应统一处理 的示例,可以根据自己的场景业务进行修改 */
if ([200, 201].includes(response.statusCode)) {
if (response.header['Transfer-Encoding']) {
return response.data
} else if (response.data.code !== 0) {
wx.showToast({ title: response.data.message || '网络错误', icon: 'none' })
return false
}
return response.data
} else {
if (response?.data?.code === 2001) {
wx.showToast({ title: '登录过期', icon: 'none', mask: true })
setTimeout(() => {
wx.navigateTo({ url: '/pages/login/login' })
}, 1000)
return false
}
// 不是主动取消的请求给出提示
if (response?.errMsg !== 'request:fail abort') {
wx.showToast({ title: '网络错误', icon: 'none' })
}
return false
}
}
export default HttpClint
tips
最后就是在小程序中各个模块中的使用了,在app.js中引入我们封装好的请求方法,最后抛出就ok了。
附app.js示例代码:
import HttpClint from './utils/request.js'
App({
$Http: HttpClint
})
附请求示例代码:
// pages/home/home.js
const App = getApp()
page({
onLoad() {
this.getDetail()
},
getDetail() {
const params = {
url: '/api/detail/get', // 请求api
method: 'POST',
data: { id: 123, type: 1 },
loading: true // 是否开启loading,可选 默认 true
// 如果有取消请求的场景,可以使用 setCancelToken 钩子进行收集,返回参数是请求实例
setCancelToken: task => { console.log(task) }
}
App.$Http.request(params).then(res => {
// 请求成功
console.log(res)
})
}
})
转载自:https://juejin.cn/post/6977247932051537933