微信小程序封装request请求,解决多个请求并发弹窗反复显示关闭问题

712 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

前言:对于程序员来说,同样的代码写两遍那就要考虑封装问题了,不是要求,就是懒不想复制,,在微信小程序开发过程中请求,封装wx.request必不可少,废话不多看代码

配置请求头

请求头里面放一些后端要求的参数,比如这里有app-type,因为牵扯到多个端,以及便于后台数据统计,后端需要一个参数进行区分请求来源,然后就是token判断是否登录的标识

 // 请求头
  let headers = {
    "content-type": "application/json",
    "app-type": "wxapp",//后端要求的
    "version": version,//版本号
  };
   if (wx.getStorageSync('wx_token')) {
    headers.token = wx.getStorageSync('wx_token')
  }

请求的配置对象,请求的路径,header,请求方式

 //请求对象 
  let requestObj = {
    url: config.Config.path + url,
    header: headers,
    method: type,
  };

请求数据data

请求携带的数据data,根据请求方式POST和PUT,单独做处理,合并到请求的配置对象中

  if (type == 'POST') {
    Object.assign(requestObj, {
      data: postData
    })
  } else if (type == 'PUT') {
    Object.assign(requestObj, {
      data: JSON.stringify(putData)
    })
  }

请求完成回调函数

然后就是处理请求之后返回的回调函数,并且合并到请求的配置对象中。

success:处理请求成功success函数,根据后端给的返回码标注,进行不同处理。
状态100开头的指定几个返回码跟token有关,10005代表账号注销跳转登录; 返回码400开头的是用于给用户提示,直接调用showToast弹出提示; 返回码1,0,5根据实时情况进行处理,这边因为是提前封装,没有针对性做处理,直接执行success函数

fail:请求失败的函数,统一做提示处理,并且打印返回的内容便于查看

complete:请求完成,无论成功失败都会处理,这里是做了一个多请求的处理,稍后再讲;

  Object.assign(requestObj, {
    success: function (res) {
      //参数值为res.data,直接将返回的数据传入
      if (res.data.new_token) {
        wx.setStorageSync('wx_token', res.data.new_token)
      }
      let status = res.data.status;
      // 1开头处理
      let loginArr = [10001, 10002, 10003, 10004, 10005];
      if (loginArr.includes(status)) {
        let statusText = status == 10005 ? '账号已冻结(注销)' : '登录过期';
        wx.showToast({
          title: statusText,
          icon: 'none',
          duration: 1500,
          success(res) {
            navigateToLogin();
          }
        })
      }
      // 4开头处理
      if (String(status).charAt(0) == 4) {
        return wx.showToast({
          title: res.data.message,
          icon: 'none',
          duration: 3000
        })
      }
      // 返回1、0、5开头,调用请求成功函数
      doSuccess(res.data);
      console.log('success', res);
    },
    fail: function (error) {
      console.log('error', error);
      if (doFail) doFail(error);
      wx.showToast({
        title: '网络繁忙',
        icon: 'none',
        duration: 3000
      })
    },
    complete: info => {
      if (doComplete) doComplete(info);
      if (reqOption.moreRequest) {
        reduceLoadingCount()
      } else {
        wx.hideLoading()
      }
    }
  })

封装request

此时所有请求的配置对象处理完毕,然后将配置对象放入wx.request中,并且声明一个函数request用于存放请求,和配置对象;
函数request参数描述
type:请求类型
url:请求地址
postData:请求参数
doSuccess:请求成功回调
doFail:请求失败回调
doComplete:请求完成回调
reqOption:配置对象{moreRequest:true,closeLoading:true}

多请求并发,以及多请求按顺序执行的loading多次弹出问题

配置对象描述:
moreRequest:布尔值,是否为多请求 closeLoading:布尔值,请求是否显示loading

closeLoading参数是为了当多个请求不是并发,且存在执行顺序关系,也会有连续多个弹窗关闭显现的问题,这时候可以使用closeLoading参数,设置为true,除了第一个执行的函数之外其余函数配置closeLoading为true,然后在最后一个函数执行的complete里面进行关闭loading,这样就达到了多个函数按照顺序执行且loading多次关闭显现的问题了。

moreRequest参数是为了解决多个请求并发,不存在执行顺序。每次调用request判断一下moreRequest的值如果存在执行addLoadingCount增加计数,在complete里面执行reduceLoadingCount减少计数,当计数为0时reduceLoadingCount里面执行wx.hideLoading()关闭弹窗 这样就解决了多请求同时并发时的弹窗多次弹出关闭问题

function request(type, url, postData, doSuccess, doFail, doComplete, reqOption) {
  // 判断是否是多请求,是都开启loading计数
  if (reqOption.moreRequest) {
    addLoadingCount
  } else {
    if (loadingCount != 0) {
      loadingCount = 0;
    }
  }
  // 判断是否关闭loading
  if(!reqOption.closeLoading){
    wx.showLoading({
      title: '加载中...',
      mask: true
    })
  }
  let requestObj = {}//配置对象
  wx.request(requestObj)
};
// loading计数
let loadingCount = 0;
// 增加请求计数
function addLoadingCount() {
  loadingCount++;
}
//减少请求计数
function reduceLoadingCount() {
  loadingCount--;
  if (loadingCount < 1) {
    wx.hideLoading()
  }
}

以上就是封装的请求,reqOption的用配置放在了最后一位,需要单独进行判断,最后一位有可能是函数,有可能是配置多请求或者关闭loading的对象,所以我们需要写一个函数用于处理请求的参数,当为GET,DELETE请求时是没有单独的请求data,以及如果只需要某个回调函数,需要我们进行判断然后赋空值占位,以免干扰请求时的判断

// 处理请求参数
function formatRequestParams(reqParams, type) {
   // 参数数组
  let paramsArr = [];
  for (let i = 0; i < reqParams.length; i++) {
    paramsArr[i] = reqParams[i]
  }
  //'GET', 'DELETE',无请求参数赋值为空
  if (['GET', 'DELETE'].includes(type)) {
    paramsArr.splice(1, 0, '')
  }
  if (paramsArr.length < 6) {
    // 计算参数位数差值,填补空值
    let diffArr = [];
    for (let i = 0; i < 6 - paramsArr.length; i++) {
      diffArr.push('');
    }
    console.log(utils.judgeDataType(paramsArr[paramsArr.length - 1]));
    if (utils.judgeDataType(paramsArr[paramsArr.length - 1]) == 'object' && JSON.stringify([paramsArr.length - 1]) != '{}') {
      paramsArr = paramsArr.slice(0, paramsArr.length - 1).concat(diffArr).concat(paramsArr.slice(paramsArr.length - 1))
    } else {
      paramsArr = paramsArr.concat(diffArr)
    }
  }
  return paramsArr;
};

函数调用

// POST请求:参数顺序url, postData, doSuccess, doFail, doComplete, reqOption
function postReq() {
  let paramsArr = ['POST'].concat(formatRequestParams(arguments, 'POST'));
  console.log('请求之后', paramsArr);
  request(...paramsArr)
}
// GET请求: 参数顺序 url, doSuccess, doFail,doComplete, reqOption
function getReq() {
  let paramsArr = ['GET'].concat(formatRequestParams(arguments, 'GET'));
  console.log('请求之后', paramsArr);
  request(...paramsArr)
}
// PUT请求:参数顺序url, postData, doSuccess, doFail, doComplete, reqOption
function putReq() {
  let paramsArr = ['PUT'].concat(formatRequestParams(arguments, 'PUT'));
  request(...paramsArr)
}
// DELETE请求:参数顺序 url, doSuccess, doFail,doComplete, reqOption
function deleteReq() {
  let paramsArr = ['DELETE'].concat(formatRequestParams(arguments, 'DELETE'));
  request(...paramsArr)
}

module.exports = {
  postReq, //post请求
  getReq, //get请求
  putReq, //put请求
  deleteReq //delete请求
}

如果需要进行网络判断可以封装一个判断网络状态的函数,将请求的函数放入判断网络状态的会调用,无网络给予提示

完整代码如下

var config = require('./config')
var utils = require('./util')
var app = getApp();
// 版本号
var version = config.Config.version
// loading计数
let loadingCount = 0;
// 增加请求计数
function addLoadingCount() {
  loadingCount++;
}
//减少请求计数
function reduceLoadingCount() {
  loadingCount--;
  if (loadingCount < 1) {
    wx.hideLoading()
  }
}
// 跳转登录
function navigateToLogin() {
  let inLogin = app.globalData.inLogin; //判断当前页面是否在登录页面
  if (!inLogin) {
    app.globalData.inLogin = true;
    wx.clearStorageSync()
    wx.navigateTo({
      url: '/pages/login/login',
    })
  }
};

/*
请求字段描述:
type:请求类型
url:请求地址
postData:请求参数
doSuccess:请求成功回调
doFail:请求失败回调
doComplete:请求完成回调
reqOption:配置对象{moreRequest:true,closeLoading:true}
配置对象描述:
moreRequest:布尔值,是否为多请求  closeLoading:布尔值,请求是否显示loading
*/

function request(type, url, postData, doSuccess, doFail, doComplete, reqOption) {
  // 判断是否是多请求,是都开启loading计数
  if (reqOption.moreRequest) {
    addLoadingCount
  } else {
    if (loadingCount != 0) {
      loadingCount = 0;
    }
  }
  // 判断是否关闭loading
  if(!reqOption.closeLoading){
    wx.showLoading({
      title: '加载中...',
      mask: true
    })
  }
  // 请求头
  let headers = {
    "content-type": "application/json",
    "app-type": "wxapp",
    "version": version,
  }
  if (wx.getStorageSync('wx_token')) {
    headers.token = wx.getStorageSync('wx_token')
  }
  //请求对象 
  let requestObj = {
    url: config.Config.path + url,
    header: headers,
    method: type,
  };
  if (type == 'POST') {
    Object.assign(requestObj, {
      data: postData
    })
  } else if (type == 'PUT') {
    Object.assign(requestObj, {
      data: JSON.stringify(putData)
    })
  }
  // 合并 请求参数和请求返回函数
  Object.assign(requestObj, {
    success: function (res) {
      //参数值为res.data,直接将返回的数据传入
      if (res.data.new_token) {
        wx.setStorageSync('wx_token', res.data.new_token)
      }
      let status = res.data.status;
      // 1开头处理
      let loginArr = [10001, 10002, 10003, 10004, 10005];
      if (loginArr.includes(status)) {
        let statusText = status == 10005 ? '账号已冻结(注销)' : '登录过期';
        wx.showToast({
          title: statusText,
          icon: 'none',
          duration: 1500,
          success(res) {
            navigateToLogin();
          }
        })
      }
      // 4开头处理
      if (String(status).charAt(0) == 4) {
        return wx.showToast({
          title: res.data.message,
          icon: 'none',
          duration: 3000
        })
      }
      // 返回1、0、5开头,调用请求成功函数
      doSuccess(res.data);
      console.log('success', res);
    },
    fail: function (error) {
      console.log('error', error);
      if (doFail) doFail(error);
      wx.showToast({
        title: '网络繁忙',
        icon: 'none',
        duration: 3000
      })
    },
    complete: info => {
      if (doComplete) doComplete(info);
      if (reqOption.moreRequest) {
        reduceLoadingCount()
      } else {
        wx.hideLoading()
      }
    }
  })
  wx.request(requestObj)
}
// 处理请求参数
function formatRequestParams(reqParams, type) {
   // 参数数组
  let paramsArr = [];
  for (let i = 0; i < reqParams.length; i++) {
    paramsArr[i] = reqParams[i]
  }
  //'GET', 'DELETE',无请求参数赋值为空
  if (['GET', 'DELETE'].includes(type)) {
    paramsArr.splice(1, 0, '')
  }
  if (paramsArr.length < 6) {
    // 计算参数位数差值,填补空值
    let diffArr = [];
    for (let i = 0; i < 6 - paramsArr.length; i++) {
      diffArr.push('');
    }
    console.log(utils.judgeDataType(paramsArr[paramsArr.length - 1]));
    if (utils.judgeDataType(paramsArr[paramsArr.length - 1]) == 'object' && JSON.stringify([paramsArr.length - 1]) != '{}') {
      paramsArr = paramsArr.slice(0, paramsArr.length - 1).concat(diffArr).concat(paramsArr.slice(paramsArr.length - 1))
    } else {
      paramsArr = paramsArr.concat(diffArr)
    }
  }
  return paramsArr;
};

// POST请求:参数顺序url, postData, doSuccess, doFail, doComplete, reqOption
function postReq() {
  let paramsArr = ['POST'].concat(formatRequestParams(arguments, 'POST'));
  console.log('请求之后', paramsArr);
  request(...paramsArr)
}
// GET请求: 参数顺序 url, doSuccess, doFail,doComplete, reqOption
function getReq() {
  let paramsArr = ['GET'].concat(formatRequestParams(arguments, 'GET'));
  console.log('请求之后', paramsArr);
  request(...paramsArr)
}
// PUT请求:参数顺序url, postData, doSuccess, doFail, doComplete, reqOption
function putReq() {
  let paramsArr = ['PUT'].concat(formatRequestParams(arguments, 'PUT'));
  request(...paramsArr)
}
// DELETE请求:参数顺序 url, doSuccess, doFail,doComplete, reqOption
function deleteReq() {
  let paramsArr = ['DELETE'].concat(formatRequestParams(arguments, 'DELETE'));
  request(...paramsArr)
}

module.exports = {
  postReq, //post请求
  getReq, //get请求
  putReq, //put请求
  deleteReq //delete请求
}