离开jQuery,运用原生Ajax
英文出处:《A Guide to Vanilla Ajax Without jQuery》
翻译: 刘健超 J.c
注重:未经许可,制止转载!
Ajax 是异步的JavaScript和XML的简称,是一种更新页面某部份的机制。它给予了你从服务器猎取数据后,更新页面某部份的权利,从而防止了革新全部页面。别的,以此体式格局完成页面部分更新,不仅能有效地打造流通的用户体验,而且减轻了服务器的负载。
下面是对一个基础的 Ajax 要求举行理会:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'send-ajax-data.php');
xhr.send(null);
在这里, 我们创建了一个能向服务器发出 HTTP 要求的类的实例。然后挪用其 open
要领,个中第一个参数是 HTTP 要求要领,第二个参数是要求页面的 URL。末了,我们挪用参数为 null 的 send
要领。假如运用 POST 要求要领(这里我们运用了 GET),那末 send 要领 的参数应当包含任何你想发送的数据。
下面是我们怎样处置惩罚服务器的响应:
xhr.onreadystatechange = function(){
var DONE = 4; // readyState 4 代表已向服务器发送要求
var OK = 200; // status 200 代表服务器返回胜利
if(xhr.readyState === DONE){
if(xhr.status === OK){
console.log(xhr.responseText); // 这是返回的文本
} else{
console.log("Error: "+ xhr.status); // 在此次要求中发作的毛病
}
}
}
onreadystatechange
是异步的,那末这就意味着它将可在任何时候被挪用。这类范例的函数被称为回调函数——一旦某些处置惩罚完成后,它就会被挪用。在此案例中,这个处置惩罚发作在服务器。
关于想进修更多关于 Ajax 基础知识的同砚,可关注 MDN 的这篇教程。
究竟挑选 jQuery 照样挑选原生 JavaScript 呢?
嗯,好消息是上述代码兼容一切最新的主流浏览器。而坏消息是运用起来十分庞杂。确实令人恶心!我已苦思出一个文雅的处理计划了。
假如运用 jQuery,则能把上述代码压缩成如许:
$.ajax({
url: "send-ajax-data.php"
}).done(function(res){
console.log(res);
}).fail(function(){
console.log("Error: " + err.status);
})
异常简约易懂。关于大多数人(我想也包含你)来讲,jQuery 已成为了处理 Ajax 的默许规范。但你晓得吗?状况不一定是如许的。jQuery 的存在是为了处理貌寝的 DOM API。但 Ajax 真的是貌寝或庞杂的吗?
在文章的盈余部份,我盘算用原生 JavaScript 使 Ajax API 有所改善。关于 Ajax 的完全文档能够在 W3C 找到。但是这份申明的题目使我异常受袭击。居然不是“XMLHttpRequest Level 2”,而是“XMLHttpRequest Level 1”——由于在2011年已将两个文档兼并。展望未来,它们将被视为单一实体,而现存规范将其称为 XMLHttpRequest。这表明社区对峙许诺只要一个规范,这关于想离开 jQuery 的开辟人员来讲,是个好消息。
所以,让我们一同最先吧!
在这篇文章,我运用 Node.js作为后端。没错,这就可以够全栈(浏览器和服务器)JS了。Node.js 是很简约的,我勉励你能在 Github下载demo,并关注该项目。下面是服务器端的代码:
// 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,肯定该app返回的响应内容。假如该要求来自 scripts
目次,那末服务器将返回内容范例(content type)为 application/javascript
的响应文件。假如要求头部的 x-requested-with
被设为 XMLHttpRequest
,那末该要求是 Ajax 要求,然后返回响应数据。除了以上两种状况,服务器将会返回 views/index.html
。
下面我将会睁开上一代码段处置惩罚 Ajax 要求的解释部份举行深切解说。在 Node.js端,我已处置惩罚了 render
和 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
函数检测 error 对象是不是存在(如:被要求文件不能被翻开,该对象就会存在)。别的,指定范例是好的做法,那末服务器返回的文件内容就会具有恰当的 HTTP 状况码(status code)和内容范例(content type)。
测试 API
让我们为后端API编写一些单元测试,从而确保它们能准确运转。关于这类测试,我会要求 supertest 和 mocha协助。
// 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);
});
这些测试确保了我们的 app 关于差别要求能返回准确的内容范例(content type)和HTTP 状况码(status code)。一旦你安装了这些依靠,那末你就可以运用敕令 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 vs onload
假如你看过任何一本威望的、关于 Ajax 的书,你能够会发明 onreadystate
在书上随处可见。该回调函数须要经由历程嵌套的 ifs 或多个 case 语句完成,这使得难以影象。让我们再次回忆 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 ev![此处输入图片的形貌][1]ent with readyState: ' +
toReadyStateDescription(oReq.readyState));
};
oReq.open('GET', e.target.dataset.url, true);
oReq.send();
});
}());
上述代码会在 掌握台(console) 输出以下语句:
onreadystatechange
事宜能在要求的任何历程中被触发。如能在每一个要求前、要求末。但依据文档,onload
事宜只会在要求胜利后触发。又由于 onload
事宜是一个罕见的 API,所以你能在很短时间内掌握它。onreadystatechange
事宜可作为后备(原文是backwards compatible 向后兼容?)计划。而 onload
事宜应当是你的首选。而且 onload
事宜与 jQuery 的 success
回调函数相似,岂非不是吗?
###设置要求头部
jQuery 私自帮你设置要求头部了,所以后端能检测这是一个 Ajax 要求。一般来讲,后端并不体贴 GET 要求是从哪而来,只要能返回准确的响应即可。当你相用一样的 web API 返回 Ajax 或 HTML 时,这就派上用场了。让我们看看怎样经由历程原生 JavaScript 设置要求头部:
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!'}));
}
正如你所看到的,原生 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 对象举行响应检测。而唯一须要注重的处所是:以小写字母读取它们。
响应范例
你能够想晓得为何 responseText
返回的是字符串,而不是能被我们操纵的一般 JSON(Plain Old JSON)。本来是由于我没有设置适宜的 responseType
属性。该 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();
哇,如许我们就没必要再对返回的纯文本剖析为 JSON 了,我们能通知 API 我们期待吸收的数据范例。该特征险些获得了一切最新主流浏览器的支撑。固然,jQuery 会自动帮我们转为恰当的范例。但如今的原生 JavaScript 也具有轻易的、完成一样事宜的要领。 原生 Ajax 已支撑许多别的响应范例,如 XML。
但遗憾的是,到 IE11 为止,开辟团队仍未对 xhr.responseType=’json’ 举行支撑。虽然该特征现在在 Microsoft Edge 获得支撑。但这个 Bug 提出险些两年了。我深信 Microsoft 团队一直在努力地革新浏览器。让我们期待 Microsoft Edge、aka Project Spartan 当初提出的许诺。
固然,你能够这个处理这个 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 要求举行缓存的浏览器特征都快被我们遗忘了。比方,IE 就默许是如许。我还曾因而致使我的 Ajax 不实行而苦恼了几个小时。荣幸的是,jQuery 默许消灭浏览器缓存。固然,你能在纯 Ajax 到达该目标,而且相称简朴:
var bustCache = '?' + new Date().getTime();
oReq.open('GET', e.target.dataset.url + bustCache, true);
检察 jQuery 文档,可晓得 jQuery 在每一个要求(GET)背面追加一个时间戳作为查询字符串。这在某个程度上让要求变得举世无双,并防止浏览器缓存。每当你触发 HTTP Ajax 要求,你能看到相似以下要求:
OK!这就没有戏剧性的事变发作了。
总结
我愿望你能喜好这篇关于原生 Ajax 的文章。Ajax 在过去某段时间里,被人们看做是一种恐怖的怪兽。而事实上,我们已覆蓋了原生 Ajax 一切基础知识。
末了,我会留给你一个简约的体式格局举行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 找到全部案例。我愿望在批评里看到你对原生 Ajax 的主意。
感谢您的浏览。
假如你以为这篇文章对您有协助或许以为我翻译得不错,那给我个 star 吧.