Ajax是异步JavaScript和XML的缩写,是一种用于进行部分页面更新的机制。 它使您可以使用来自服务器的数据来更新页面的各个部分,同时避免进行完全刷新。 以这种方式进行部分更新可以有效地创造流畅的用户体验,并减少服务器上的负载。
这是基本的Ajax请求的剖析:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'send-ajax-data.php');
xhr.send(null);
在这里,我们正在创建必需类的实例,以向服务器发出HTTP请求。 然后,我们调用其open
方法,将HTTP请求方法指定为第一个参数,将我们请求的页面的URL指定为第二个参数。 最后,我们调用其send
方法,将null作为参数传递。 如果发布请求(此处使用GET),则此参数应包含我们要与请求一起发送的任何数据。
这就是我们如何处理服务器的响应:
xhr.onreadystatechange = function () {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
console.log(xhr.responseText); // 'This is the returned text.'
} else {
console.log('Error: ' + xhr.status); // An error occurred during the request.
}
}
};
onreadystatechange
是异步的,这意味着可以随时调用它。 这些类型的函数是回调-在某些处理完成后将被调用。 在这种情况下,正在服务器上进行处理。
对于那些希望了解有关Ajax基础的更多信息的人,MDN网络提供了很好的指南 。
到jQuery还是不到jQuery?
因此,好消息是上述代码将在所有最新的主要浏览器上运行。 坏消息是,它令人费解。 ! 我已经在寻求一个优雅的解决方案。
使用jQuery,可以将整个代码段压缩为:
$.ajax({
url: 'send-ajax-data.php',
})
.done(function(res) {
console.log(res);
})
.fail(function(err) {
console.log('Error: ' + err.status);
});
很好 对于包括您在内的许多人来说,确实如此,当涉及到Ajax时,jQuery已成为事实上的标准。 但是,你知道吗? 不一定是这种情况。 jQuery的存在是为了避开难看的DOM API。 但是,它真的是丑? 还是难以理解?
在本文的其余部分中,我想研究对香草JavaScript中Ajax API所做的改进。 整个规范可以在W3C上找到。 这个规范令我印象深刻的是名称。 它不再是“ XMLHttpRequest Level 2”,而是“ XMLHttpRequest Level 1”,这是两个规范在2011年合并的结果。 展望未来,从标准的角度来看,它将被视为单个实体,并且现有的标准将称为XMLHttpRequest 。 这表明社区致力于遵循一个标准,这对于希望摆脱jQuery的开发人员而言仅意味着好消息。
因此,让我们开始吧……
设定
对于本文,我在后端使用Node.js。 是的,浏览器和服务器上都将包含JavaScript。 Node.js后端很精简,我建议您在GitHub上下载整个演示并继续。 这是服务器上的肉和土豆:
// app.js
var app = http.createServer(function (req, res) {
if (req.url.indexOf('/scripts/') >= 0) {
render(req.url.slice(1), 'application/javascript', httpHandler);
} else if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
// Send Ajax response
} else {
render('views/index.html', 'text/html', httpHandler);
}
});
这将检查请求URL,以确定应用程序应如何响应。 如果请求来自scripts
目录,那么将使用application/javascript
的内容类型来提供适当的文件。 否则,如果请求的x-requested-with
标头已设置为XMLHttpRequest
那么我们知道我们正在处理Ajax请求,并且可以适当地响应。 并且如果都不是这种情况,则将提供views/index.html
文件。
在我们深入研究服务器的Ajax响应时,我将扩展注释掉的部分。 在Node.js中,我不得不对render
和httpHandler
做一些繁重的httpHandler
:
// app.js
function render(path, contentType, fn) {
fs.readFile(__dirname + '/' + path, 'utf-8', function (err, str) {
fn(err, str, contentType);
});
}
var httpHandler = function (err, str, contentType) {
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occured: ' + err.message);
} else {
res.writeHead(200, {'Content-Type': contentType});
res.end(str);
}
};
render
函数异步读取请求文件的内容。 它传递了对httpHandler
函数的引用,然后将其作为回调执行。 httpHandler
函数检查是否存在错误对象(例如,如果无法打开请求的文件,则该错误对象将存在)。 提供一切都很好,然后使用适当的HTTP状态代码和内容类型为文件的内容提供服务。
测试API
像使用任何声音后端API一样,让我们编写一些单元测试以确保其正常工作。 对于这些测试,我呼吁超级测试和摩卡咖啡寻求帮助:
// test/app.request.js
it('responds with html', function (done) {
request(app)
.get('/')
.expect('Content-Type', /html/)
.expect(200, done);
});
it('responds with javascript', function (done) {
request(app)
.get('/scripts/index.js')
.expect('Content-Type', /javascript/)
.expect(200, done);
});
it('responds with json', function (done) {
request(app)
.get('/')
.set('X-Requested-With', 'XMLHttpRequest')
.expect('Content-Type', /json/)
.expect(200, done);
});
这些可以确保我们的应用以正确的内容类型和HTTP状态代码响应不同的请求。 一旦安装了依赖项,就可以使用npm test
从命令运行这些npm test
。
介面
现在,让我们看一下我们正在用HTML构建的用户界面:
// views/index.html
<h1>Vanilla Ajax without jQuery</h1>
<button id="retrieve" data-url="/">Retrieve</button>
<p id="results"></p>
HTML看起来很简洁。 如您所见,所有激动都发生在JavaScript中。
onreadystate与onload
如果您读过任何经典的Ajax书籍,那么到处都有onreadystate
。 该回调函数带有嵌套的ifs和大量的绒毛,使您难以记住自己的头部。 让我们将onreadystate
和onload
事件放在一起。
(function () {
var retrieve = document.getElementById('retrieve'),
results = document.getElementById('results'),
toReadyStateDescription = function (state) {
switch (state) {
case 0:
return 'UNSENT';
case 1:
return 'OPENED';
case 2:
return 'HEADERS_RECEIVED';
case 3:
return 'LOADING';
case 4:
return 'DONE';
default:
return '';
}
};
retrieve.addEventListener('click', function (e) {
var oReq = new XMLHttpRequest();
oReq.onload = function () {
console.log('Inside the onload event');
};
oReq.onreadystatechange = function () {
console.log('Inside the onreadystatechange event with readyState: ' +
toReadyStateDescription(oReq.readyState));
};
oReq.open('GET', e.target.dataset.url, true);
oReq.send();
});
}());
这是控制台中的输出:
onreadystate
事件在各处触发。 它在每个请求的开始,结束时触发,有时只是因为它真的喜欢被触发。 但是根据规范, onload
事件仅在请求成功时才触发。 因此, onload
事件是一种现代API,您可以在几秒钟内充分利用它。 onreadystate
事件是向后兼容的。 但是, onload
事件应该是您选择的工具。 onload
事件看起来像jQuery上的success
回调,不是吗?
现在是时候将5磅重的哑铃放在一边,然后继续弯曲手臂。
设置请求头
jQuery在后台设置了请求标头,因此您的后端技术知道这是Ajax请求。 通常,只要发送适当的响应,后端就不在乎GET请求来自何处。 当您想使用相同的Web API支持Ajax和HTML时,这非常方便。 因此,让我们看一下如何在香草Ajax中设置请求标头:
var oReq = new XMLHttpRequest();
oReq.open('GET', e.target.dataset.url, true);
oReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
oReq.send();
这样,我们可以在Node.js中进行检查:
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({message: 'Hello World!'}));
}
如您所见,vanilla Ajax是一种灵活而现代的前端API。 您可以使用大量的请求标头,其中之一就是版本控制。 例如,假设我要支持此Web API的多个版本。 当我不想破坏URL而是提供一种机制,使客户端可以选择所需的版本时,这很有用。 我们可以这样设置请求标头:
oReq.setRequestHeader('x-vanillaAjaxWithoutjQuery-version', '1.0');
在后端,请尝试:
if (req.headers['x-requested-with'] === 'XMLHttpRequest' &&
req.headers['x-vanillaajaxwithoutjquery-version'] === '1.0') {
// Send Ajax response
}
Node.js为您提供了一个headers
对象,可用于检查请求标头。 唯一的技巧是它以小写形式读取它们。
我们到家了,还没有流汗! 您可能想知道,关于Ajax还有什么要了解的? 好吧,有几个巧妙的技巧。
回应类型
您可能想知道,当我使用的是普通的旧JSON时,为什么responseText
包含服务器响应。 原来是因为我没有设置正确的reponseType
。 这个Ajax属性非常适合告诉前端API服务器期望什么类型的响应。 因此,让我们充分利用它:
var oReq = new XMLHttpRequest();
oReq.onload = function (e) {
results.innerHTML = e.target.response.message;
};
oReq.open('GET', e.target.dataset.url, true);
oReq.responseType = 'json';
oReq.send();
令人敬畏的是,我可以告诉API期望什么,而不必发送回然后我必须将其解析为JSON的纯文本。 几乎所有最新的主要浏览器都提供此功能。 jQuery当然会自动进行这种类型的转换。 但是,现在有了一种在普通JavaScript中执行相同操作的便捷方法,这不是很好吗? Vanilla Ajax支持许多其他响应类型,包括XML。
可悲的是,在Internet Explorer中,这个故事并不那么出色。 从IE 11开始,该团队尚未添加对xhr.responseType ='json'的支持 。 此功能将在Microsoft Edge上提供 。 但是,截至撰写本文时,该错误已将近两年未解决。 我的猜测是Microsoft的人们一直在努力改进浏览器。 我们希望Microsoft Edge(又名Project Spartan)能够兑现其诺言。
las,如果您必须解决此IE问题:
oReq.onload = function (e) {
var xhr = e.target;
if (xhr.responseType === 'json') {
results.innerHTML = xhr.response.message;
} else {
results.innerHTML = JSON.parse(xhr.responseText).message;
}
};
快取清除
人们往往会忘记的一种浏览器功能是缓存Ajax请求的功能。 例如,Internet Explorer默认情况下会执行此操作。 我曾经费时数小时试图弄清楚为什么我的Ajax不能正常工作。 幸运的是,jQuery默认会破坏浏览器缓存。 好吧,您也可以使用纯Ajax,这非常简单:
var bustCache = '?' + new Date().getTime();
oReq.open('GET', e.target.dataset.url + bustCache, true);
根据jQuery文档 ,它所做的就是将时间戳查询字符串附加到请求的末尾。 这使请求有些独特,并破坏了浏览器缓存。 您可以看到在触发HTTP Ajax请求时的样子:
多田! 全部没有戏剧。
结论
我希望您喜欢300lb卧推香草Ajax。 曾几何时,阿贾克斯是一只可怕的野兽,但仅此而已。 实际上,我们已经涵盖了Ajax的所有基础知识,而没有jQuery的cru脚,hem锁。
我将以简洁的方式向您发出Ajax调用:
var oReq = new XMLHttpRequest();
oReq.onload = function (e) {
results.innerHTML = e.target.response.message;
};
oReq.open('GET', e.target.dataset.url + '?' + new Date().getTime(), true);
oReq.responseType = 'json';
oReq.send();
这是响应的样子:
别忘了,您可以在GitHub上找到整个演示。 我很乐意在评论中听到您在使用和不使用jQuery时的想法。
From: https://www.sitepoint.com/guide-vanilla-ajax-without-jquery/