没有jQuery的香草Ajax指南
异步JavaScript和XML的简称,Ajax是一种进行部分页面更新的机制。它使您能够使用来自服务器的数据更新页面的各个部分,同时避免了完全刷新的需要。以这种方式进行部分更新可以有效地创建流畅的用户体验,并可以减少服务器上的负载。
这是一个基本Ajax请求的剖析:
var xhr = new XMLHttpRequest();xhr。开放('GET', 'send-ajax-data.php'); xhr.send(null);
在这里,我们正在创建所需类的实例,以便向服务器发出HTTP请求。我们称之为开放
方法,将HTTP请求方法指定为第一个参数,将我们请求的页面的URL指定为第二个参数。最后,我们称其为发送
方法将null作为参数传递。如果post请求(这里我们使用GET),这个参数应该包含我们想要随请求发送的任何数据。
这是我们如何处理来自服务器的响应:
xhr。onreadystatechange = function () {var DONE = 4;// readyState 4表示请求完成。var OK = 200;// status 200返回成功如果(xhr。readyState === DONE) {if (xhr。status === OK) {console.log(xhr.responseText);// '这是返回的文本'}其他{console.log('错误:' + 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(函数(err) {console.log('错误:' + err.status);});
这很好。事实上,对于很多人来说,jQuery已经成为Ajax的实际标准。但是,你知道吗?这并不一定是这样的。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){渲染(req.url.slice(1), '应用程序/javascript', httpHandler);} else if (req。头['x-requested-with'] === 'XMLHttpRequest') { // Send Ajax response } else { render('views/index.html', 'text/html', httpHandler); } });
这将检查请求URL,以确定应用程序应该如何响应。如果请求来自脚本
目录,则提供内容类型为的适当文件应用程序/ javascript
.否则,如果请求为x-requested-with
头文件已设置为XMLHttpRequest
那么我们就知道我们正在处理一个Ajax请求,我们可以做出适当的响应。如果这两种情况都不是,那文件视图/ index . html
是服务。
在深入讨论来自服务器的Ajax响应时,我将展开被注释掉的部分。在Node.js中,我必须对渲染
而且httpHandler
:
// app.js函数渲染(path, contentType, fn) {fs. jsreadFile(__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('一个错误已经发生:' + err.message);} else {res.writeHead(200, {'Content-Type': contentType});res.end (str);}};
的渲染
函数异步读取所请求文件的内容。对象的引用传递给httpHandler
函数,然后作为回调函数执行。的httpHandler
函数检查是否存在错误对象(例如,如果请求的文件无法打开,则会出现错误对象)。如果一切正常,它将使用适当的HTTP状态代码和内容类型提供文件的内容。
测试API
与任何合理的后端API一样,让我们编写一些单元测试来确保它能够工作。为了这些测试,我要求supertest而且摩卡寻求帮助:
// test/app.request.js it('响应html',函数(完成){request(app) .get('/') .expect('Content-Type', /html/) .expect(200,完成);});it('用javascript响应',function (done) {request(app) .get('/scripts/index.js') .expect('Content-Type', /javascript/) .expect(200, done);});it(' response with json', function (done) {request(app) .get('/') .set('X-Requested-With', 'XMLHttpRequest') .expect('Content-Type', /json/) .expect(200, done);});
这确保我们的应用程序以正确的内容类型和HTTP状态代码响应不同的请求。安装依赖项之后,可以使用命令运行这些测试npm测试
.
的接口
现在,让我们来看看我们在HTML中构建的用户界面:
/ /视图/ index . html<h1>没有jQuery的香草Ajaxh1><按钮id="检索"data-url="/">检索按钮><pid="结果">p>
HTML看起来很整洁。如您所见,所有令人兴奋的事情都发生在JavaScript中。
Onreadystate vs onload
如果您阅读任何有关Ajax的权威书籍,您可能会发现onreadystate
无处不在。这个回调函数带有嵌套的if和大量的绒毛,这使得你很难立即记住它。让我们把onreadystate
而且onload
事件针锋相对。
(function () {var retrieve = document.getElementById('retrieve'), results = document.getElementById('results'), toReadyStateDescription = function (state) {switch (state) {case 0:返回'UNSENT';case 1:返回'OPENED';case 2:返回'HEADERS_RECEIVED';case 3:返回'LOADING';case 4:返回'DONE';默认值:返回";}};检索。addEventListener('点击',函数(e) {var oReq =新的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
event是一个可以在几秒钟内充分利用的现代API。的onreadystate
事件是向后兼容的。但是,onload
事件应该是您选择的工具。的onload
事件看起来像成功
jQuery回调,不是吗?
是时候把5磅的哑铃放在一边,开始练习手臂弯曲了。
设置请求报头
jQuery在后台设置请求头,以便后端技术知道这是一个Ajax请求。通常,后端并不关心GET请求来自何处,只要它发送正确的响应即可。当你想用相同的web API支持Ajax和HTML时,这就很方便了。那么,让我们看看如何在普通Ajax中设置请求头:
var oReq = new XMLHttpRequest();oReq。开放('GET', e.target.dataset.url, true); oReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); oReq.send();
有了这个,我们可以在Node.js中进行检查:
如果(点播。头['x-requested-with'] === 'XMLHttpRequest') { res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({message: 'Hello World!'})); }
如您所见,普通Ajax是一种灵活而现代的前端API。有很多想法可以使用请求头,其中之一是版本控制。举个例子,假设我想支持多个版本的web API。当我不想破坏url,而是提供一种机制,让客户端可以选择他们想要的版本时,这很有用。我们可以像这样设置请求头:
oReq。setRequestHeader(“x-vanillaAjaxWithoutjQuery-version”、“1.0”);
在后端,尝试:
如果(点播。头['x-requested-with'] === 'XMLHttpRequest' && req.headers['x-vanillaajaxwithoutjquery-version'] === '1.0') { // Send Ajax response }
Node.js给你一个头
对象,可用于检查请求标头。唯一的技巧是用小写字母来读。
我们已经到了最后阶段,还没流汗呢!您可能想知道,关于Ajax还有什么需要了解的呢?来点小技巧吧。
响应类型
你可能想知道为什么响应结果字符串
包含服务器响应时,我所使用的是普通的JSON。结果是,这是因为我没有设置适当的reponseType
.这个Ajax属性非常适合告诉前端API期望从服务器得到什么类型的响应。所以,让我们好好利用这个:
var oReq = new XMLHttpRequest();oReq。Onload =函数(e){结果。innerHTML = e.target.response.message;};oReq。开放('GET', e.target.dataset.url, true); oReq.responseType = 'json'; oReq.send();
太棒了,而不是发送回纯文本,然后我必须解析成JSON,我可以告诉API期望什么。这个特性在几乎所有最新的主流浏览器中都可用。当然,jQuery会自动完成这种类型的转换。但是,现在我们有了一种在纯JavaScript中做同样事情的方便方法,这不是很棒吗?Vanilla Ajax支持许多其他响应类型,包括XML。
遗憾的是,在ie浏览器中,这个故事就没那么精彩了。截至IE 11,团队还没有添加支持xhr。responseType = ' json '.这一特性即将到来微软的优势.但是,在撰写本文时,这个漏洞已经存在了近两年。我猜微软的人一直在努力改进浏览器。希望微软Edge(又名Project Spartan)能兑现承诺。
唉,如果你必须要解决IE的问题:
oReq。Onload =函数(e) {var XHR = e.target;如果(xhr。responseType === 'json'){结果。innerHTML = xhr.response.message;} else{结果。innerHTML = JSON.parse(xhr.responseText).message;}};
缓存的地沟油
人们容易忘记的一个浏览器特性是缓存Ajax请求的功能。例如,Internet Explorer在默认情况下是这样做的。我曾经挣扎了几个小时试图弄清楚为什么我的Ajax不能工作因为这个。幸运的是,jQuery默认情况下会破坏浏览器缓存。好吧,你也可以在纯Ajax,它是相当直接的:
var bustCache = '?' + new Date().getTime();oReq。开放('GET', e.target.dataset.url + bustCache, true);
根据jQuery文档,它所做的只是在请求的末尾追加一个时间戳查询字符串。这使得请求在某种程度上是唯一的,并破坏了浏览器缓存。你可以看到当你触发HTTP Ajax请求时是什么样子的:
大作。一切都没有戏剧性。
结论
我希望你喜欢300磅的香草阿贾克斯卧推。曾几何时,阿贾克斯是一只可怕的野兽,但现在不是了。事实上,我们已经介绍了Ajax的所有基础知识,没有jQuery的束缚。
我将留给您一种简洁的Ajax调用方法:
var oReq = new XMLHttpRequest();oReq。Onload =函数(e){结果。innerHTML = e.target.response.message;};oReq。开放('GET', e.target.dataset.url + '?' + new Date().getTime(), true); oReq.responseType = 'json'; oReq.send();
回复是这样的:
别忘了,你可以在上面找到完整的演示GitHub.我欢迎在评论中听到你对Ajax和不使用jQuery的看法。