刚入门前端的新手们,一定遇到过这样的场景:用户提交表单后整个页面突然刷新,所有填写的内容瞬间清空;或者想在页面上展示实时数据,却不得不让用户手动点击刷新按钮。这种体验就像每次看电视换台都要重启电视机一样令人抓狂。
AJAX技术正是为了解决这些问题而生的。作为现代Web开发的基石技术之一,它让网页能够在不刷新的情况下与服务器悄悄"对话",只更新需要变化的部分。想象一下,你在电商网站筛选商品时,价格区间滑块拖动后立即看到结果,而不需要等待页面重载——这就是AJAX的魔力。
传统HTTP请求就像每次去图书馆借书都要重新进大门、过安检、出示证件。而AJAX则像是有了专属图书管理员,你只需坐在位置上递出书单,管理员就会把需要的书籍直接送到你面前。
技术层面上,传统请求会触发浏览器默认行为:
AJAX的工作流程则是:
现代浏览器提供了两种主要的AJAX实现方式:
javascript复制// 经典XHR方式
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/data');
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById('content').innerHTML = xhr.responseText;
}
};
xhr.send();
// 更现代的Fetch API
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log(data);
});
提示:虽然Fetch API更现代,但在处理进度事件和取消请求时,XHR仍然有其优势。实际项目中建议根据需求选择。
我们先搭建一个简单的开发环境:
基础GET请求示例:
javascript复制document.getElementById('loadBtn').addEventListener('click', () => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
.then(data => {
document.getElementById('output').innerText = JSON.stringify(data, null, 2);
});
});
处理表单提交是AJAX的典型应用场景:
javascript复制document.forms['userForm'].addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
fetch('/api/users', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
showNotification('用户创建成功!');
});
});
这个例子将展示如何实现输入时即时显示搜索结果:
javascript复制const searchInput = document.getElementById('search');
let controller;
searchInput.addEventListener('input', () => {
// 取消之前的请求
if (controller) controller.abort();
controller = new AbortController();
fetch(`/api/search?q=${searchInput.value}`, {
signal: controller.signal
})
.then(res => res.json())
.then(results => {
renderResults(results);
});
});
高频触发的事件(如滚动、输入)需要优化:
javascript复制function debounce(func, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, arguments), delay);
};
}
searchInput.addEventListener('input', debounce(searchHandler, 300));
健壮的AJAX实现需要考虑:
javascript复制async function fetchWithRetry(url, options = {}, retries = 3) {
try {
const response = await fetch(url, options);
if (!response.ok) throw new Error(response.statusText);
return response;
} catch (err) {
if (retries <= 0) throw err;
await new Promise(res => setTimeout(res, 1000));
return fetchWithRetry(url, options, retries - 1);
}
}
良好的UI反馈至关重要:
javascript复制function toggleLoader(show) {
document.getElementById('loader').style.display = show ? 'block' : 'none';
}
fetch('/api/data')
.then(() => toggleLoader(false))
.catch(() => toggleLoader(false));
当遇到CORS错误时,可以:
javascript复制// 前端设置withCredentials
fetch(url, {
credentials: 'include'
});
// 后端示例设置
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
为请求添加超时控制:
javascript复制function fetchWithTimeout(url, options = {}, timeout = 5000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
)
]);
}
未完成的请求可能造成内存泄漏:
javascript复制let activeRequests = new Set();
function safeFetch(url, options) {
const controller = new AbortController();
const request = fetch(url, { ...options, signal: controller.signal });
activeRequests.add(controller);
request.finally(() => activeRequests.delete(controller));
return request;
}
// 组件卸载时取消所有请求
function cleanup() {
activeRequests.forEach(controller => controller.abort());
activeRequests.clear();
}
虽然原生AJAX仍然有效,但现代项目更多使用:
一个使用Axios的示例:
javascript复制axios.get('/api/user', {
params: { ID: 12345 }
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
在实际项目中,我通常会根据项目规模选择方案。小型项目直接用Fetch足够,中型项目引入Axios,大型复杂应用则会考虑React Query等状态管理方案。记住,技术选型要考虑团队熟悉度和长期维护成本,而不仅仅是技术本身的新颖程度。