前言

在日常工作中,我经常使用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. jQuery中attr()函数 VS prop()函数

    http://www.365mini.com/page/jquery-attr-vs-prop.htm 在jQuery中,attr()函数和prop()函数都用于设置或获取指定的属性,它们的参数和用法 ...

  2. 菜鸟vimer成长记——第2.1章、normal模式

    目的 掌握normal模式下常用操作的语法和概念,这些操作对应的应用场景以及实用技巧. 通过normal模式举一反三掌握cmd-line和visual的常用文本操作. 简介 文本操作的理想状态为:一个 ...

  3. javascript中encodeURI和decodeURI方法使用介绍

    encodeURI和decodeURI是成对来使用的,因为浏览器的地址栏有中文字符的话,可以会出现不可预期的错误, 所以可以encodeURI把非英文字符转化为英文编码,decodeURI可以用来把字 ...

  4. list add() 和 addall()的区别

    http://blog.tianya.cn/post-4777591 如果有多个已经被实例化的List 集合,想要把他们组合成一个整体,并且,这里必须直接使用List 自身提供的一个方法List.ad ...

  5. 常见的浏览器端的存储技术:cookie

    工作原理: cookie是存在用户硬盘中,用户每次访问站点时,Web应用程序都可以读取Cookie包含的信息.当用户再次访问这个站点时,浏览器就会在本地硬盘上查找与该 URL 相关联的 Cookie. ...

  6. [PLC]ST语言五:STL/RET/CMP/ZCP

    一:STL/RET/CMP/ZCP 说明:简单的顺控指令不做其他说明. 控制要求:无 编程梯形图: 结构化编程ST语言: (*步进指令STL(EN,s);*) SET(M8002,S3); STL(T ...

  7. 判断浏览器是chrome,Opera,Safari,Mac

    function(){ return { isSafari: (navigator.userAgent.indexOf('Safari')>=0 || navigator.userAgent.i ...

  8. python类与对象的内置函数大全(BIF)

    关于类与对象的一些常用BIF(内置函数) 1.issubclass(class,classinfo) 含义:如果class是classinfo的子类,则返回True,否则返回false,用来判断子类关 ...

  9. [Processing]在画布上写文本

    准备工作 这一步只是我强迫症犯了哈,这个随意,画几根线而已.每一小格10个像素,中格50,大格100像素 void setup() { size(,); } void draw() { backgro ...

  10. 你这一辈子要用到的C数学函数都在这

       两数相加  #include <stdio.h> int main(void){ int a = 10;  //定义变量a, 把10 赋值给a int b = 20;  //定义变量 ...