模仿 JQuery 封装的一个简单的 Ajax 库,进行实战练习,深入理解 Ajax,复习库和插件的封装。
参数设置
options: [Object]
type: [String] 请求方式,默认 get
url: [String] 请求地址
data: [Object] 发送的数据
dataType: [String] 数据类型,json/xml/text,默认 json
cache: [Boolean] 是否缓存,默认不缓存
async: [Boolean] 同步或异步,默认异步
success: [Function] 请求成功的回调,传入获取的数据和 XHR 对象
error: [Function] 请求失败的回调,传入错误信息和 XHR 对象
整体结构
使用一个闭包封装,然后通过挂载到 window 来使用,借用了 JQuery 的思想
;(function(window) {
function ajax(options) {
//...
}
window.ajax = ajax;
})(window)
同样借助 JQuery 的思想,实现 ajax 既可以用作普通函数,又可以用作构造函数,使用都会返回一个 init 实例(也就是 ajax 实例)
function ajax(options) {
return new init(options); //=> 返回
}
let init = function init(options) {
//...
};
ajax.prototype = {
constructor: ajax,
init,
...
};
init.prototype = ajax.prototype;
构造函数
首先解析出配置对象,并且赋予默认值,然后挂载到实例上
let init = function init(options = {}) {
let {
url,
type = 'get',
data = null,
dataType = 'json',
async = true,
cache = false,
success,
error
} = options;
//=> 把配置项挂载到实例上
['url', 'type', 'data', 'dataType', 'async', 'cache', 'success', 'error'].forEach(item => {
this[item] = eval(item);
});
}
发送 Ajax 请求
分为传统的四步,中间增加了一些过程,这是一个主要的方法,必须执行这个方法才能
sendAjax() {
this.handleData(); //=> 处理发送的数据
this.handleCache(); //=> 处理缓存问题
let {
type,
url,
data,
async,
error,
success
} = this;
//=> 第一步
xhr = new XMLHttpRequest();
//=> 第二步
xhr.open(type, url, async);
//=> 第三步
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//=> error
if (!/^(2|3)\d{2}$/.test(xhr.status)) {
error && error(xhr.statusText, xhr);
return;
}
//=> success
let result = this.handleDataType(xhr); //=> 根据数据类型,处理响应数据
success && success(result, xhr);
}
};
//=> 设置请求头,使得后台接受的参数是 form data,而不是字符串
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=utf-8');
//=> 第四步
xhr.send(data);
}
处理相关参数
处理 dataType
只处理了 text、json、xml 格式的数据
handleDataType(xhr) {
let dataType = this.dataType.toUpperCase(),
result = xhr.responseText;
//=> 这里只处理了 三种 格式
switch (dataType) {
case 'TEXT':
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
break;
}
return result;
}
处理 data
首先,传递的数据如果是对象,需要转换为字符串
然后,根据请求方式的不同,传递的方式不同
get 类:拼接到 URL 上,发送的数据为 null
post 类:直接将字符串的数据发送出去
handleData() {
let {
data,
type
} = this;
if (!data) return;
// 如果是对象,转换为 x-www-form-urlencoded 模式,方便后期传递给服务器
if (typeof data === 'object') {
let str = ``;
for (let key in data) {
if (data.hasOwnProperty(key)) {
str += `${key}=${data[key]}&`
}
}
data = str.slice(0, str.length - 1);
}
// 根据请求方式的不同,传递数据的方式不同
if (/^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i.test(type)) {
this.url += `${this.checkURL()}${data}`;
this.data = null;
return;
}
this.data = data;
}
处理 cache
只有 get 请求需要进行处理,如果不需要缓存,那么需要在 URL 后面的问号传参追加一个随机参数(这里是时间戳)
handleCache() {
let {
type,
url,
cache
} = this;
if (/^GET$/i.test(type) && cache === false) {
//=> url 末尾追加时间戳
url += `${this.checkURL()}_=${+(new Date())}`;
this.url = url;
}
}
其他辅助方法
检测 URL中是否存在问号
checkURL() {
return this.url.indexOf('?') > -1 ? '&' : '?';
}
代码
;(function (window) {
function ajax(options) {
return new init(options);
}
let init = function init(options = {}) {
let {
url,
type = 'get',
data = null,
dataType = 'json',
async = true,
cache = false,
success,
error
} = options;
//=> 把配置项挂载到实例上
['url', 'type', 'data', 'dataType', 'async', 'cache', 'success', 'error'].forEach(item => {
this[item] = eval(item);
});
}
ajax.prototype = {
constructor: ajax,
//=> 初始化以及作为构造函数
init,
//=> 发送 Ajax 请求
sendAjax() {
this.handleData();
this.handleCache();
let {
type,
url,
data,
async,
error,
success
} = this,
xhr = new XMLHttpRequest();
xhr.open(type, url, async);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//=> error
if (!/^(2|3)\d{2}$/.test(xhr.status)) {
error && error(xhr.statusText, xhr);
return;
}
//=> success
let result = this.handleDataType(xhr);
success && success(result, xhr);
}
};
xhr.send(data);
},
//=> 处理 dataType
handleDataType(xhr) {
let dataType = this.dataType.toUpperCase(),
result = xhr.responseText;
//=> 这里只处理了 三种 格式
switch (dataType) {
case 'TEXT':
break;
case 'JSON':
result = JSON.parse(result);
break;
case 'XML':
result = xhr.responseXML;
break;
}
return result;
},
//=> 处理 cache
handleCache() {
let {
type,
url,
cache
} = this;
if (/^GET$/i.test(type) && cache === false) {
//=> url 末尾追加时间戳
url += `${this.checkURL()}_=${+(new Date())}`;
this.url = url;
}
},
//=> 处理 data
handleData() {
let {
data,
type
} = this;
if (!data) return;
// 如果是对象,转换为 x-www-form-urlencoded 模式,方便后期传递给服务器
if (typeof data === 'object') {
let str = ``;
for (let key in data) {
if (data.hasOwnProperty(key)) {
str += `${key}=${data[key]}&`
}
}
data = str.slice(0, str.length - 1);
}
// 根据请求方式的不同,传递数据的方式不同
if (/^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i.test(type)) {
this.url += `${this.check()}${data}`;
this.data = null;
return;
}
this.data = data;
},
//=> 检测 URL中是否存在问号
checkURL() {
return this.url.indexOf('?') > -1 ? '&' : '?';
}
}
//=> 借鉴 JQuery,使得 new ajax 或者直接使用 ajax 都会返回 ajax 的实例
init.prototype = ajax.prototype;
window.ajax = ajax;
})(window)