前言

在日常工作中,我经常使用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. JAVA 删除指定目录下指定文件类型的所有文件

    public class DelFile { public static void main(String[] args) { File file = new File("C:\\DETEC ...

  2. iOS分类Category探索

    什么是Category? Category是Objective-C 2.0之后添加的语言特性,Category的主要作用是为已经存在的类添加方法,一般称为分类,文件名格式是"NSObject ...

  3. SICP读书笔记 2.4

    SICP CONCLUSION 让我们举起杯,祝福那些将他们的思想镶嵌在重重括号之间的Lisp程序员 ! 祝我能够突破层层代码,找到住在里计算机的神灵! 目录 1. 构造过程抽象 2. 构造数据抽象 ...

  4. python破解网吧收费系统,远控网吧电脑设备!

      我今天呢 , 我就没事跟着朋友喝酒喝酒啊.喝了很多啊.晚上到旁边的酒店开了一个房间,到了酒店才十点! 感觉没啥事情干的,那就去网吧走走看把,看到是一个嘟嘟牛的,和上次是一样的.还是照常用MS170 ...

  5. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

  6. 软件RAID

    软件RAID也必须在多磁盘系统中才能实现.实现RAID1最少要拥有两块硬盘,而实现RAID5则最少要拥有三块硬盘.通常情况下,操作系统所在磁盘采用RAID1,而数据所在磁盘采用RAID5.   卷的类 ...

  7. 琴声不等式--jensen

    (来自百度百科) 1. 凹函数,不加权 2. 凹函数,加权 3. 凸函数,不加权 4. 凸函数,加权 应用 在EM算法Q函数的推导中,用到了第二个不等式(凹函数,加权)

  8. MySQL基础练习(二)

    第一个例子我们编写一个 SQL 查询,列出所有超过或等于5名学生的课. 先建表 CREATE TABLE courses( student ) NOT NULL, class ) NOT NULL ) ...

  9. OSSEC 架构

    OSSEC由很多部分组成,它有一个集中的管理端,用于监控.并接收来自代理.syslog.数据库或无代理设备的日志. 管理端(服务器) 管理端属于OSSEC部署中的中心部分.它存储了文件完整性检测数据库 ...

  10. Python 中的一些小技巧

    这里是本人收集的一些 Python 小技巧,目前主要是一些实用函数,适合有一定基础的童鞋观看(不会专门介绍使用到的标准库函数).. 一.函数式编程 函数式编程用来处理数据,感觉很方便.(要是再配上管道 ...