在这篇文章,我使用 Node.js作为后端。没错,这就可以全栈(浏览器和服务器)JS了。Node.js 是很简洁的,我鼓励你能在 Github下载demo,并关注该项目。下面是服务器端的代码:
= 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 的体力活:
rush:js;">
// app.js
function render(path,contentType,fn) {
fs.readFile(__dirname + '/' + path,'utf-8',function (err,str) {
fn(err,str,contentType);
});
}
var httpHandler = function (err,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帮助。
rush:js;">
// 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
代码:
rush:xhtml;">
// views/index.html
Vanilla Ajax without jQuery
上述的 HTML 代码看起来很简洁。没错,正如你所看到的,所有令人兴奋的事情都发生在 JavaScript。
onreadystate vs onload
如果你看过任何一本权威的、关于 Ajax 的书,你可能会发现 onreadystate 在书上随处可见。该回调
函数需要通过嵌套的 ifs 或多个 case 语句完成,这使得难以记忆。让我们再次回顾 onreadystate 和 onload 事件。
rush:js;">
(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 设置请求头部:
rush:js;">
var oReq = new XMLHttpRequest();
oReq.open('GET',true);
oReq.setRequestHeader('X-Requested-With','XMLHttpRequest');
oReq.send();
与此同时,我们在 Node.js 做一个检测:
rush: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,取而代之的是:通过设置请求头部,使客户端能选择它们想要的版本。所以,我们能这样设置请求头部:
rush:js;">
oReq.setRequestHeader('x-vanillaAjaxWithoutjQuery-version','1.0');
然后,在后端写上相应代码:
rush:js;">
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 所期望服务器返回的数据类型。所以,我们要好好利用它:
rush:js;">
var oReq = new XMLHttpRequest();
oReq.onload = function (e) {
results.innerHTML = e.target.response.message;
};
oReq.open('GET',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 问题:
rush:js;">
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 达到该目的,而且相当简单:
rush:js;">
var bustCache = '?' + new Date().getTime();
oReq.open('GET',e.target.dataset.url + bustCache,true);
查看 jQuery 文档,可知道 jQuery 在每个请求(GET)后面追加一个时间戳作为查询字符串。这在某个程度上让请求变得独一无二,并避免浏览器缓存。每当你触发 HTTP Ajax 请求,你能看到类似如下请求:
OK!这就没有戏剧性的事情发生了。
总结
我希望你能喜欢这篇关于原生 Ajax 的
文章。Ajax 在过去某段时间里,被人们看作是一种可怕的怪兽。而事实上,我们已经覆盖了原生 Ajax 所有基础知识。
最后,我会留给你一个简洁的方式进行Ajax调用:
rush:js;">
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();
原文地址:https://www.jb51.cc/ajax/48875.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。