本文主要介绍用Promise对apicloud中的ajax模块进行简单的封装,并对开发过程中未登录拦截/网络中断拦截类问题提供一种简单的处理方法.由于本人知识限制,可能用的方法有点笨拙,还望各位大侠指正。
ajax的简单封装
由于apicloud自带api.ajax()方法使用起来较于繁复,本节主要尝试用现阶段较为流行es6中的Promise对apicloud中的ajax模块进行简单的封装。
实现
// 默认配置对象
var options = {
// 基础url前缀
baseUrl: '',
// 默认错误处理函数
defErrHandle: err => {throw new Error(JSON.stringify(err))},
// 默认成功处理函数
defSueHandle: () => {},
// 响应成功拦截器
interceptorResSuc: (res, fn) => fn(res.data),
// 响应失败拦截器
// 默认把错误信息和出错接口向上抛出
interceptorResErr: (err, res, rej) => rej({...err.data, url: err.config.url}),
// 请求拦截器
interceptorReq: config => config
}
class ApiAjax {
/**
* 构造方法
* @param {object} config 配置参数
*/
constructor (config) {
options = Object.assign(options, config);
}
/**
* 封装了apicloud中的ajax方法
* @param {object} arg apicloud ajax控制对象
* @return {Promise} Promise对象
*/
ajax (arg) {
let _arg = Object.assign({}, arg);
arg.url = options.baseUrl + arg.url;
// _Promise为Promise一个封装对象
return new _Promise((res, rej) => {
// 调用请求拦截器
let config = options.interceptorReq(arg);
// 调用apicloud中的ajax方法
!!config && api.ajax(config, (ret, err) => {
// 将结果传入对应的拦截器中,并把最后的成功/失败判定权转移到连接器中
if (!!ret) {
options.interceptorResSuc({data: ret, config: _arg}, res, rej);
}
else {
options.interceptorResErr({data: err, config: _arg}, res, rej);
}
})
})
}
}
复制代码
本段代码为ApiAjax类核心方法ajax的封装,后续的一些粒子方法如get/post/put/delete等,都是通过修改ajax方法传入的参数进行封装,这里就不再做过多介绍.
运用
在开发过程,特别是在移动端,有时会出现网络不稳定的现象,在这时候用户请求数据时,我们常常把这些请求储存起来,并弹出一个网络出错页面提示用户,等到网络重新链接时,把储存的请求重新请求.或者在一些需要登录后才能使用功能的开发中,我们需要把未登录用户发起的请求储存起来,并弹出登录页,等到用户登录后再把储存的请求自动请求一次.本节用网络中断拦截为例,首先上代码:
// 是否有设置网络错误监听器
let isNetListener = false;
// 存放网络错误ajax请求
let netAjaxArr = [];
// 存放网络错误ajax结果处理函数
let netResArr = [];
let ajax = new ApiAjax({
interceptorResErr: (err, res, rej) => {
let { data, config } = err;
// 拦截网络错误响应
if (data.statusCode === 0) {
// 广播网络错误
api.sendEvent({name: 'netError'})
// ajax请求数组与ajax结果处理函数数组必须一一对应
// 这里请求数组与结果处理函数数组用队列模型处理
netAjaxArr.push(config);
netResArr.push(res);
// 开启对应的监听器
// 监听器要对所有ajax请求数组中的请求遍历访问,并把结果交由对应的结果处理函数处理
// 并把相应的请求从数组中清除
if (!isNetListener) {
api.addEventListener({ name: 'network' }, () => {
let arrLength = netAjaxArr.length;
for (let i = 0; i < arrLength; i++) {
// 用闭包函数或者let处理异步问题
let handle = netResArr.shift();
ajax.ajax(netAjaxArr.shift()).then(ret => {
handle(ret);
// 关闭网络错误页
api.closeWin({ name: 'network_error' });
});
}
})
// 把网络监听器的标志设为打开状态
isNetListener = true;
}
// 打开网络错误页
// 这里需要通过延时解决 openWin() 失效bug
setTimeout(() => api.openWin({
name: 'network_error',
url: 'network_error.html',
slidBackEnabled: 'false',
bgColor: '#fff',
animation: {
type: 'none'
}
}), 500);
} else {
rej({
...data,
url: config.url
});
}
}
});
复制代码
以上时ajax的配置,首先创建两个队列(数组),一个存放ajax的请求,一个存放ajax的成功处理函数,在网络错误时,分别把请求和处理函数存入数组中并订阅了一个重新请求的全局事件,当事件触发时,重新请求请求数组里的所有请求.最后便是打开一个网络错误的提示页.
网络提示页必须完成网络状态监听跟触发重新请求事件,代码如下
// 设置网络状态监听
setNetworkListener: function() {
api.addEventListener({
name: 'online'
}, this.retry);
},
// 重试
retry: function() {
api.sendEvent({
name: 'network'
})
}
复制代码
坑点
这种设计一个明显的缺陷就是只能储存ajax请求,而一些资源请求如图片加载(<img src="http://xxxx">)无法储存,当网络恢复正常后页面如果不刷新图片会产生破图现象.