本文详细讲解如何使用原生JavaScript实现AJAX功能,结合Node.js后端处理。内容涵盖服务器端路由与响应类型判断、单元测试配置、onload与onreadystatechange事件对比、请求头部设置、响应类型处理、缓存规避及IE兼容性问题,提供完整代码示例与最佳实践。
Node.js后端环境搭建与路由处理
本文使用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目录,服务器将返回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对象是否存在(例如文件无法打开时),同时指定正确的Content-Type,确保服务器返回的文件拥有合适的HTTP状态码和Content-Type。
API单元测试配置与运行
测试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);
});
这些测试确保应用对不同请求返回正确的Content-Type和HTTP状态码。安装相关依赖后,可通过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回调函数。该回调需通过嵌套if或多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();
});
}());
上述代码在控制台输出如下信息:

onreadystatechange事件在请求各阶段触发,包括请求前和请求末。根据文档,onload事件仅在请求成功时触发。作为常见API,onload易于掌握,而onreadystatechange可作为向后兼容的备用方案。优先推荐使用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?因未设置合适的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(原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请求时,可见类似如下请求格式:

至此,无其他复杂操作。
总结 希望读者喜欢这篇关于原生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();