前言

在日常工作中,我经常使用Jquery的Ajax来获取接口数据。这几天有一个的官网要制作,由于网站比较小,有一些与服务器通信的接口处理,并没有涉及到复杂的交互功能。为了可以减少加载Jquery库的时间,也不用负担Jquery复杂的逻辑处理带来的性能消耗。我决定不使用jquery,自己写了一个原生的Ajax函数。

需求整理

一般来说,前端与服务器的通信是使用XHR对象的。我做的官网是有几个异域的接口,然而XHR对象是没有跨域功能的。所以我把JSONP整合进来。
接下来,我们来看看整体功能图:
流程图

输入参数

首先,我们定义一个Ajax函数,并设置了一些输入参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
function ajax(options){

    var url = options.url || "", //请求的链接
type = (options.type || "get").toLowerCase(), //请求的方法,默认为get
data = options.data || null, //请求的数据
contentType = options.contentType || "", //请求头
dataType = options.dataType || "", //请求的类型
async = options.async === undefined && true, //是否异步,默认为true.
timeOut = options.timeOut, //超时时间。
before = options.before || function(){}, //发送之前执行的函数
error = options.error || function(){}, //错误执行的函数
success = options.success || function() {}; //请求成功的回调函数
}

参数表:

参数 默认值 描述 可选值
url “” 请求的链接 string
type get 请求的方法 get,post
data null 请求的数据 object,string
contentType “” 请求头 string
dataType “” 请求的类型 jsonp
async true 是否异步 blooean
timeOut undefined 超时时间 number
before function(){} 发送之前执行的函数 function
error function(){} 请求报错执行的函数 function
success function(){} 请求成功的回调函数 function

编码

一般来说,发送到后端的数据,若是包括中文或某些标点符号时,就要对发送的数据进行编码了。

  • 如果data为字符串,通过&分割,对键名与键值分别编码
  • 如果data为对象,把键值转化为字符串,再进行编码
  • 由于encodeURIComponent不对+编码,所以我们用replace方法手动编码
  • 若是使用get方法或JSONP,则数据是通过URL参数的方法传到后台,所以我们手动添加数据到URL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//编码数据
function setData() {
var name, value;
if (data) {
if (typeof data === "string") {
data = data.split("&");
for (var i = 0, len = data.length; i < len; i++) {
name = data[i].split("=")[0];
value = data[i].split("=")[1];
data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
}
data = data.replace("/%20/g", "+");
} else if (typeof data === "object") {
var arr = [];
for (var name in data) {
var value = data[name].toString();
name = encodeURIComponent(name);
value = encodeURIComponent(value);
arr.push(name + "=" + value);
}
data = arr.join("&").replace("/%20/g", "+");
}
//若是使用get方法或JSONP,则手动添加到URL中
if (type === "get" || dataType === "jsonp") {
url += url.indexOf("?") > -1 ? data : "?" + data;
}
}
}

XMLHttpRequerst

  • 创建XHR对象,并针对IE进行兼容性处理
  • 调用XHR的open方法,设置请求的方法,请求的链接,是否异步
  • 设置请求头
  • 添加监听,如果成功则执行success函数,报错则执行error函数
  • 调用XHR的send方法,发送数据。如果是get方法,我们已经通过setData方法把数据添加到URL中了,所以这里data设置为null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function createXHR() {
//由于IE6的XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。
//所以创建XHR对象,需要在这里做兼容处理。
function getXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
//遍历IE中不同版本的ActiveX对象
var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
for (var i = 0; i < versions.length; i++) {
try {
var version = versions[i] + ".XMLHTTP";
return new ActiveXObject(version);
} catch (e) {}
}
}
}
//创建对象。
xhr = getXHR();
xhr.open(type, url, async);
//设置请求头
if (type === "post" && !contentType) {
//若是post提交,则设置content-Type 为application/x-www-four-urlencoded
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
} else if (contentType) {
xhr.setRequestHeader("Content-Type", contentType);
}
//添加监听
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { success(xhr.responseText);
} else {
error(xhr.status, xhr.statusText);
}
}
};
//发送请求
xhr.send(type === "get" ? null : data); }

JSONP

  • 创建script标签
  • 设置回调函数名称
  • 监听回调函数
  • 设置URL,并添加到文档中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建JSONP
function createJsonp() {
var script = document.createElement("script"),
timeName = new Date().getTime() + Math.round(Math.random() * 1000),
callback = "JSONP_" + timeName; window[callback] = function(data) {
document.body.removeChild(script);
success(data);
}
script.src = url + (url.indexOf("?") > -1 ? "" : "?") + "callback=" + callback;
script.type = "text/javascript";
document.body.appendChild(script);
}

超时设置

  • 设置一个全局的定时器标识,用来在回调函数中清除定时器
  • JSONP
    • 传入两个参数,一个是回调函数名,一个是script标签
    • 超时之后,移除监听函数,移除script标签
  • XHR
    • 超时之后,调用XHR的abort方法,停止请求
    • 由于执行abort()方法后,有可能触发onreadystatechange事件,所以设置一个timeout_bool标识,来忽略中止触发的事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var timeout_flag = null, //定时器标识
timeout_bool = false;//是否请求超时
//设置请求超时
function setTime(callback, script) {
if (timeOut !== undefined) {
timeout_flag = setTimeout(function() {
if (dataType === "jsonp") {
delete window[callback];
document.body.removeChild(script); } else {
timeout_bool = true;
xhr && xhr.abort();
}
console.log("timeout"); }, timeOut);
}
}

添加超时函数,并设置在回调成功后移除定时器

JSONP

1
2
3
4
5
6
7
8
9
10
11
12
// 创建JSONP
function createJsonp() {
……
window[callback] = function(data) {
clearTimeout(timeout_flag);
document.body.removeChild(script);
success(data);
}
……
document.body.appendChild(script);
setTime(callback, script);
}

XHR

function createXHR() {
……
//添加监听
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (timeOut !== undefined) {
//由于执行abort()方法后,有可能触发onreadystatechange事件,
//所以设置一个timeout_bool标识,来忽略中止触发的事件。
if (timeout_bool) {
return;
}
clearTimeout(timeout_flag);
}
……
}
};
//发送请求
xhr.send(type === "get" ? null : data);
setTime(); //请求超时
}

全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
function ajax(options) {
//编码数据
function setData() {
var name, value;
if (data) {
if (typeof data === "string") {
data = data.split("&");
for (var i = 0, len = data.length; i < len; i++) {
name = data[i].split("=")[0];
value = data[i].split("=")[1];
data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value);
}
data = data.replace("/%20/g", "+");
} else if (typeof data === "object") {
var arr = [];
for (var name in data) {
var value = data[name].toString();
name = encodeURIComponent(name);
value = encodeURIComponent(value);
arr.push(name + "=" + value);
}
data = arr.join("&").replace("/%20/g", "+");
}
//若是使用get方法或JSONP,则手动添加到URL中
if (type === "get" || dataType === "jsonp") {
url += url.indexOf("?") > -1 ? data : "?" + data;
}
}
}
// JSONP
function createJsonp() {
var script = document.createElement("script"),
timeName = new Date().getTime() + Math.round(Math.random() * 1000),
callback = "JSONP_" + timeName; window[callback] = function(data) {
clearTimeout(timeout_flag);
document.body.removeChild(script);
success(data);
}
script.src = url + (url.indexOf("?") > -1 ? "" : "?") + "callback=" + callback;
script.type = "text/javascript";
document.body.appendChild(script);
setTime(callback, script);
}
//设置请求超时
function setTime(callback, script) {
if (timeOut !== undefined) {
timeout_flag = setTimeout(function() {
if (dataType === "jsonp") {
delete window[callback];
document.body.removeChild(script); } else {
timeout_bool = true;
xhr && xhr.abort();
}
console.log("timeout"); }, timeOut);
}
} // XHR
function createXHR() {
//由于IE6的XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。
//所以创建XHR对象,需要在这里做兼容处理。
function getXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
//遍历IE中不同版本的ActiveX对象
var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"];
for (var i = 0; i < versions.length; i++) {
try {
var version = versions[i] + ".XMLHTTP";
return new ActiveXObject(version);
} catch (e) {}
}
}
}
//创建对象。
xhr = getXHR();
xhr.open(type, url, async);
//设置请求头
if (type === "post" && !contentType) {
//若是post提交,则设置content-Type 为application/x-www-four-urlencoded
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
} else if (contentType) {
xhr.setRequestHeader("Content-Type", contentType);
}
//添加监听
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (timeOut !== undefined) {
//由于执行abort()方法后,有可能触发onreadystatechange事件,
//所以设置一个timeout_bool标识,来忽略中止触发的事件。
if (timeout_bool) {
return;
}
clearTimeout(timeout_flag);
}
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { success(xhr.responseText);
} else {
error(xhr.status, xhr.statusText);
}
}
};
//发送请求
xhr.send(type === "get" ? null : data);
setTime(); //请求超时
} var url = options.url || "", //请求的链接
type = (options.type || "get").toLowerCase(), //请求的方法,默认为get
data = options.data || null, //请求的数据
contentType = options.contentType || "", //请求头
dataType = options.dataType || "", //请求的类型
async = options.async === undefined && true, //是否异步,默认为true.
timeOut = options.timeOut, //超时时间。
before = options.before || function() {}, //发送之前执行的函数
error = options.error || function() {}, //错误执行的函数
success = options.success || function() {}; //请求成功的回调函数
var timeout_bool = false, //是否请求超时
timeout_flag = null, //超时标识
xhr = null; //xhr对角
setData();
before();
if (dataType === "jsonp") {
createJsonp();
} else {
createXHR();
}
}

源码下载

源码放在github中了。欢迎下载,地址

本文摘自:littleBlack

原生Ajax函数的更多相关文章

  1. 原生ajax函数封装

    原生ajax函数 function ajax(json){ json=json || {}; if(!json.url){ return; } json.data=json.data || {}; j ...

  2. 原生ajax解析&封装原生ajax函数

    前沿:对于此篇随笔,完是简要写了几个重要的地方,具体实现细节完在提供的源码做了笔记 <一>ajax基本要点介绍--更好的介绍ajax 1. ajax对象中new XMLHttpReques ...

  3. 轻量级原生 ajax 函数,支持 get/array post/array post/json

    原生js封装 function ajaxRequest(type, url, data, callback, failCallBack, header, dataType) { var url_enc ...

  4. 原生Ajax

    使用原生Ajax 验证用户名是否被注册 创建出注册信息: <h1>注册信息</h1><input type="text" name="txt ...

  5. JS原生ajax与Jquery插件ajax深入学习

    序言: 近来随着项目的上线实施,稍微有点空闲,闲暇之时偶然发现之前写的关于javascript原生xmlHttpRequest ajax方法以及后来jquery插件ajax方法,于是就行了一些总结,因 ...

  6. Ajax_04之jQuery中封装的Ajax函数

    1.PHP中json_encode编码规则: PHP索引数组编码为JSON:[...] PHP关联数组编码为JSON:{...}2.jQuery中AJAX封装函数之load: ①使用:$('选择器') ...

  7. 浅谈AJAX的基本原理和原生AJAX的基础用法

    一.什么是AJAX? AJAX,即"Asynchronous Javascript And XML",翻译为异步的JavaScript和XML,是一种创建交互式网页应用的网页开发技 ...

  8. jQuery.ajax() 函数详解

    jQuery.ajax()函数用于通过后台HTTP请求加载远程数据. jQuery.ajax()函数是jQuery封装的AJAX技术实现,通过该函数,我们无需刷新当前页面即可获取远程服务器上的数据. ...

  9. python_way day21 Django文件上传Form方式提交,原生Ajax提交字符处啊,Django文件上传之原生Ajax方式、jQuery Ajax方式、iframe方式,Django验证码,抽屉示例,

    python_way day21 1.Django文件上传至Form方式 2.原生Ajax文件上传提交表单 使用原生Ajax好处:不依赖jquery,在发送一个很小的文件或者字符串的时候就可以用原生A ...

随机推荐

  1. SSISDB3:Package的执行实例

    SSISDB 系列随笔汇总: SSISDB1:使用SSISDB管理Package SSISDB2:SSIS工程的操作实例 SSISDB3:Package的执行实例 SSISDB4:当前正在运行的Pac ...

  2. html5新特性data_*自定义属性使用

    HTML5规范里增加了一个自定义data属性. 这个自定义data属性的用法非常的简单, 就是你可以往HTML标签上添加任意以 "data-"开头的属性, 这些属性页面上是不显示的 ...

  3. springmvc框架开发常用的注解总结

    1.@Controller使用:表示表现层中的JavaBean被spring容器管理.   2.@requestMapping使用: a) 在方法上: 标记url到请求方法的映射, 就相当于从一个ur ...

  4. xml解析数据信息并实现DBManager操作mysql

      先前一直都是用的直接用加载驱动 然后创建连接进行操作数据 如果我的数据库换了 那么要修改的地方也比较多 不利于维护 所以就想到了将所有配置连接信息都用xml封装起来 以至于我每次都只要修改一下我的 ...

  5. mac指令备忘

    在这里简单记录下最近使用的快捷键,备忘,随时更新. 简单指令记录 mkdir 创建路径 pwd 输出当前路径 ls 查看目录 cd touch 创建文件 tree 输出目录树 mv 源文件 目标文件或 ...

  6. Scrum Meeting 10.22

    Scrum Meeting No.2 今天的主要任务是配置安卓开发环境,并运行上一届的项目. 主流的安卓开发环境有eclipse+ADT+SDK和android studio两种.两种环境的文件架构似 ...

  7. No.1_NABCD模型分析

        Reminder 之 NABCD模型分析           定位 多平台的闹钟提醒软件. 在安卓市场发布软件,发布后一周的用户量为1000.           N (Need 需求) 这个 ...

  8. 团队冲刺——Three

    第三天计划: 季方:学习爬虫的操作,以便后续功能实现: 司宇航:对当天实现的功能进行总的测试: 王金萱:数据库内数据的增删改查以及查看团队博客界面的实现: 马佳慧:学习css初步,进行页面绘制: 第二 ...

  9. CS小分队第二阶段冲刺站立会议(6月2日)

    昨日成果:攻克了按钮移动的问题: 遇到问题:一开始按钮移动时候,非常慢,因为是根绝相对位移差来移动,延时很严重,后来改用用鼠标的位置作为按钮的移动位置,效果明显. 按钮的mousedown事件和mou ...

  10. Task 6.2冲刺会议九 /2015-5-22

    今天把之前的跳转问题加以改正并加以优化.遇到的主要问题是跳转的时后时间有点长,以为没有成功.之后查资料说是端口没有及时释放,导致了程序的滞缓.明天要继续把程序的界面进行优化.