一、AJAX基础概念与核心原理
AJAX(Asynchronous JavaScript and XML)是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容的异步通信技术。尽管名称中包含“XML”,但现代AJAX开发中JSON已成为更主流的数据交换格式。
核心原理包括以下关键环节:
- 创建XMLHttpRequest对象:浏览器提供原生API用于发起HTTP请求
- 配置请求参数:设置HTTP方法、URL、是否异步等
- 设置回调函数:监听readyState变化及响应完成事件
- 发送请求:通过send()方法将请求体数据(如有)发送至服务器
- 处理响应数据:在回调中读取responseText或responseXML并更新DOM
值得注意的是,AJAX并非新语言或框架,而是JavaScript与浏览器API协同工作的技术模式。其本质是通过事件驱动方式实现异步通信,极大提升了用户体验和页面响应效率。
常见误解澄清
许多开发者误以为AJAX必须使用XML格式,这是历史遗留认知。实际上:
- JSON作为轻量级数据格式,解析效率更高,已成为主流选择
- Text/plain、form-data、multipart等格式也广泛用于特定场景
- 现代浏览器支持responseType属性,可直接返回Blob、ArrayBuffer等二进制数据
此外,AJAX请求默认遵循同源策略,跨域场景需通过CORS、JSONP或代理方案解决——这将在后续章节深入探讨。
二、XMLHttpRequest深度解析与面试要点
XMLHttpRequest(XHR)是AJAX技术的核心基础,其API设计虽古老但功能完备。面试中常考察其状态码、属性方法及使用规范。
核心状态码与属性
XHR对象有5个readyState状态值,对应请求生命周期的不同阶段:
| 状态码 | 含义 | 关键特征 |
|---|---|---|
| 0 | UNSENT | 对象已创建,未调用open() |
| 1 | OPENED | open()已调用,可设置请求头 |
| 2 | HEADERS_RECEIVED | send()已调用,响应头已接收 |
| 3 | LOADING | 响应体部分接收,可分块处理 |
| 4 | DONE | 响应完成,可安全读取数据 |
关键属性包括:
- responseText:字符串形式的响应体
- responseXML:DOM解析后的XML文档(若Content-Type为application/xml)
- status:HTTP状态码(如200、404、500)
- statusText:HTTP状态描述(如"OK"、"Not Found")
- responseType:设置响应数据类型(如"json"、"blob")
- withCredentials:跨域请求时是否携带凭证(cookie等)
典型代码示例
// GET请求示例
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users', true);
// 监听状态变化
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log('用户数据:', data);
} else {
console.error('请求失败:' + xhr.status);
}
}
};
xhr.send();
高级技巧:进度监听与超时控制
现代XHR支持进度事件和超时设置,适用于大文件上传/下载场景:
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.timeout = 5000; // 5秒超时
// 进度监听
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`上传进度:${percent.toFixed(1)}%`);
}
};
xhr.onload = function() { ... };
xhr.ontimeout = function() { console.error('请求超时'); };
xhr.onerror = function() { console.error('网络错误'); };
xhr.send(formData);
三、Fetch API:现代化异步请求方案
Fetch API是基于Promise的新型请求方式,提供更简洁的语法和强大的功能,已成为现代前端开发的主流选择。相比XHR,它具有以下优势:
- 基于Promise,支持async/await语法,代码更易读
- 统一的请求/响应对象模型
- 更完善的流式处理能力
- 自动处理CORS预检请求
基础用法与响应处理
// GET请求
async function fetchUsers() {
try {
const response = await fetch('/api/users', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer token123'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('用户列表:', data);
} catch (error) {
console.error('请求失败:', error);
}
}
请求体与FormData处理
// POST请求(JSON格式)
const userData = {
name: '张三',
email: 'zhangsan@example.com'
};
await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
// POST请求(FormData格式)
const formData = new FormData();
formData.append('name', '李四');
formData.append('avatar', fileInput.files[0]);
await fetch('/api/upload', {
method: 'POST',
body: formData
// 注意:不手动设置Content-Type,浏览器会自动添加boundary参数
});
流式响应处理
Fetch支持读取流式响应,适用于实时数据传输场景:
const response = await fetch('/stream/events');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
for (const line of chunk.split('\n')) {
if (line.trim()) {
console.log('收到事件:', JSON.parse(line));
}
}
}
四、跨域问题解决方案与实战对比
跨域是AJAX开发中最常见的问题,源于浏览器的同源策略限制。所谓“同源”需满足:协议、域名、端口三者完全一致。面试中常要求分析不同解决方案的适用场景与优缺点。
常见跨域场景
以下情况均构成跨域请求:
- http://example.com → https://example.com(协议不同)
- http://example.com → http://api.example.com(子域名不同)
- http://example.com:8080 → http://example.com:9000(端口不同)
- http://example.com → http://localhost:3000(域名不同)
解决方案对比表
| 方案 | 原理 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| CORS | 服务端设置Access-Control-Allow-Origin | 所有现代浏览器环境 | 支持所有HTTP方法、传递凭证、标准化方案 | 需服务端配合、预检请求开销 |
| JSONP | 利用 |