封装一
在各种前端框架如React、VUE、Angular等层出不穷的今天,前端的开发效率大大提高,且前端在用原始技术开发时的许多问题在使用了框架后也不存在了,个人在使用了vue开发项目后,觉得这些框架真的太好了,真的给我们节省了很多的开发时间。话虽如此,但依然有很多的前端小伙伴在使用基于jquery的前端技术,也包括本人。
那么问题来了,前端在开发时势必会调后端的接口,就会用到ajax,就会在调接口时可能会有各种状态展示,于是,封装一个公共的ajax来实现产品的需求就显得很有必要。接下来我们就来实现对jquery的ajax进行二次封装。
/*
* type 请求的方式 默认为get
* url 发送请求的地址
* param 发送请求的参数
* isShowLoader 是否显示loader动画 默认为false
* dataType 返回JSON数据 默认为JSON格式数据
* callBack 请求的回调函数
*/
(function(){
function AjaxRequest(opts){
this.type = opts.type || "get";
this.url = opts.url;
this.param = opts.param || {};
this.isShowLoader = opts.isShowLoader || false;
this.dataType = opts.dataType || "json";
this.callBack = opts.callBack;
this.init();
}
AjaxRequest.prototype = {
//初始化
init: function(){
this.sendRequest();
},
//渲染loader
showLoader: function(){
if(this.isShowLoader){
var loader = '<div class="ajaxLoader"><div class="loader">加载中...</div></div>';
$("body").append(loader);
}
},
//隐藏loader
hideLoader: function(){
if(this.isShowLoader){
$(".ajaxLoader").remove();
}
},
//发送请求
sendRequest: function(){
var self = this;
$.ajax({
type: this.type,
url: this.url,
data: this.param,
dataType: this.dataType,
beforeSend: this.showLoader(),
success: function(res){
self.hideLoader();
if (res != null && res != "") {
if(self.callBack){
if (Object.prototype.toString.call(self.callBack) === "[object Function]") { //Object.prototype.toString.call方法--精确判断对象的类型
self.callBack(res);
}else{
console.log("callBack is not a function");
}
}
}
}
});
}
};
window.AjaxRequest = AjaxRequest;
})();
页面调用:
<script>
new AjaxRequest({
type: "get",
url: "https://5ae979d7531a580014142797.mockapi.io/api/v1/records",
param: "",
isShowLoader: true,
dataType: "",
callBack: function(res){
console.log(res);
}
});
</script>
之前本篇博文所描述的对ajax的二次封装虽然也实现了同样的功能,但却加入了大量的判断,比如要判断某个参数是否传入,如果没有传入,就要给它一个默认值,这还是其次,麻烦的是如果这个参数没有传入,那么它就会被后续的参数所覆盖,由此就还要判断后续的参数的类型以防止参数用错等等,这不仅大大增加了代码量,还降低了性能,在提升页面性能需求的今天,至少我认为这是不可取的,而且我在网上搜了一把,发现基本所有对ajax的二次封装都是加入了对参数的各种判断。
而且这种的封装方式本人私心想着可能是要求在调用这个方法的时候就必须每个参数都要传入,哪怕是传入了一个空字符串。如果不传,就会被后边的参数所覆盖。比如图1中的data如果没有传入,data就会被success参数所覆盖,此时data=success,那么在图2中success就会被当作ajax的data参数传给接口。其实呢,success是我们传入的一个函数参数,是要用作ajax的回调函数来触发的,不是当作ajax的参数传给接口的。(上边三张图是截自网络,没有恶意,如果需要,可以私信我删除)
于是,面向对象的封装方式我认为还是可以很好的解决以上的种种问题,也大大降低了封装的成本,提升了性能(至少这种方式不会出现由于前边的参数没有传入而被后边的参数所覆盖的问题)。
封装二
function ajax(opt){
var defaultSettings = {
data: {},
type: 'get',
dataType: 'json',
headers:{'Authorization': getStore('token')},
beforeSend: function(){
openLoad('loading.gif')
},
complete:function(){
closeLoad();
},
error: function(xhr){
if(xhr.status == 401){
showTips('您未登录')
}else if(xhr.status == 403){
showTips('您没有权限')
}else if(xhr.status == 500){
showTips('服务器发生错误,请稍后刷新页面')
}else{
showTips('未知错误')
}
}
}
for(var key in opt){
defaultSettings[key] = opt[key]
if(key == 'url')
defaultSettings[key] = '/api/' + opt[key] + '?format=json&time=' + new Date().getTime()
}
if(arguments[1]){ //如果是图片上传,需要传递第二个参数,必须内容
defaultSettings.traditional = true
defaultSettings.processData = false
defaultSettings.contentType = false
}
return $.ajax(defaultSettings)
}
function showTips(msg){
var tipsEl = $('<div></div>').text(msg).css({
"text-align":"center",
"line-height":"40px",
"height":'40px',
"background":'rgba(0,0,0,0.5)',
'position':'fixed',
"left":'50%',
"top":'50%',
"transform":'translate(-50%,-50%)',
"z-index":999999,
'padding':'15px 30px',
"box-sizing":'border-box',
"display":'none',
"font-size":'14px'
})
tipsEl.appendTo($('body')).fadeIn(500).delay(1000).fadeOut(500,function(){
tipsEl.remove()
})
}
function getStore (name) {
if (!name) return;
return window.localStorage.getItem(name);
}
function openLoad( src ){
var loadEl_icon = $('<i></i>').css({
"width":'50px',
"height":'50px',
"margin":'auto',
"background":'url(' + src + ') no-repeat center center',
'background-size':'cover'
})
var lodeEl_inner = $('<div class="ui-loading-inner"></div>').css({
"width":'200px',
"height":'200px',
"margin":'auto',
"border-radius":'5px',
"background":'rgba(0,0,0,0.5)',
"display":'flex'
}).append(loadEl_icon)
var loadEl_wrap = $('<div class="ui-loading-mask"></div>').css({
"position":'absolute',
"left":0,"top":0,"bottom":0,"right":0,
"background":'rgba(255,255,255,0.01)',
"z-index":999999,
"display":'flex'
}).append(lodeEl_inner).appendTo($('body'))
}
function closeLoad(){
$('.ui-loading-mask').remove()
}
在封装里默认进行了error函数的判断,也提供了页面消息提示框,调用方式如下:
ajax(formData,true) //上传图片
ajax(opt)//普通调用
如果需要多次回调,可以这样来调用:
//参数opt的设置
var opt = {
url:'do_login',
type:'post',//如果是post需要传递type,如果是get可以不传递
data:{usrname,password},
success(r){
//your code here or callback(r)
}
}
ajax(opt).then((r)=>{
console.log(r)
)
看网上有许多大神都封装成了面向对象的方式,这里没有采用的原因是,ajax在一个项目中会多次使用,面向对象封装或许要采用单例模式,否则会增加程序开销,但是发现网上大多数的封装都没有注意这个问题,另外,将loading组件和提示组件放进一个对象里,可能增加代码的耦合性,单独使用不方便。
另外,代码里所有的css都应该在 .css 样式文件里定义好,这样直接创建一个元素就不用设置样式了,这里为了方便表达所以才写在了js里,用的时候可以单独拆分出来,所以这里的代码只提供一个思路,具体可以结合业务进行相应改动。