当前位置: 首页 > news >正文

ajax学习手册

Ajax 通俗易懂学习手册

目录

  1. Ajax 基础概念
  2. XMLHttpRequest 详解
  3. Fetch API (现代方式)
  4. 处理不同数据格式
  5. 错误处理和状态码
  6. Ajax 高级技巧
  7. 实战项目案例
  8. 最佳实践

Ajax 基础概念

什么是 Ajax?

Ajax = Asynchronous JavaScript And XML

通俗解释: Ajax 就像"外卖小哥",你在网页上点了个按钮(下单),Ajax 悄悄跑到服务器那里取数据(送餐),拿回来后更新页面(送到你手上),整个过程你不用刷新页面!

Ajax 的优势

  • 无需刷新页面 - 用户体验更好
  • 节省带宽 - 只传输需要的数据
  • 提高性能 - 减少服务器负担
  • 实时交互 - 即时获取最新数据

Ajax 能做什么?

// 常见应用场景
- 搜索建议(输入时实时显示)
- 无限滚动(微博、朋友圈)
- 表单验证(检查用户名是否存在)
- 购物车更新(不刷新页面添加商品)
- 聊天应用(实时收发消息)
- 天气查询(获取实时天气)

XMLHttpRequest 详解

基础用法 - 一步步学会

// 第1步:创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest();// 第2步:配置请求
xhr.open('GET', 'https://api.example.com/users', true);
// 参数说明:
// - 'GET': 请求方法
// - URL: 请求地址
// - true: 是否异步(几乎总是true)// 第3步:设置响应处理
xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {console.log('请求成功!');const data = JSON.parse(xhr.responseText);console.log(data);}
};// 第4步:发送请求
xhr.send();

理解 readyState(请求状态)

// readyState 的5种状态
0: UNSENT - 请求未初始化
1: OPENED - 连接已建立
2: HEADERS_RECEIVED - 请求已接收
3: LOADING - 请求处理中
4: DONE - 请求已完成// 实际使用中的状态检查
xhr.onreadystatechange = function() {console.log('当前状态:', xhr.readyState);if (xhr.readyState === 4) {if (xhr.status === 200) {console.log('成功!', xhr.responseText);} else {console.log('出错了,状态码:', xhr.status);}}
};

GET 请求完整示例

function getData(url, callback) {const xhr = new XMLHttpRequest();xhr.open('GET', url, true);// 设置请求头(可选)xhr.setRequestHeader('Content-Type', 'application/json');xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {const data = JSON.parse(xhr.responseText);callback(null, data); // 成功} else {callback(new Error(`请求失败: ${xhr.status}`)); // 失败}}};xhr.send();
}// 使用示例
getData('https://jsonplaceholder.typicode.com/posts', (error, data) => {if (error) {console.error('出错了:', error.message);} else {console.log('获取到的数据:', data);}
});

POST 请求 - 发送数据

function postData(url, data, callback) {const xhr = new XMLHttpRequest();xhr.open('POST', url, true);// POST 请求必须设置这个头部xhr.setRequestHeader('Content-Type', 'application/json');xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200 || xhr.status === 201) {const response = JSON.parse(xhr.responseText);callback(null, response);} else {callback(new Error(`请求失败: ${xhr.status}`));}}};// 发送 JSON 数据xhr.send(JSON.stringify(data));
}// 使用示例:创建新用户
const newUser = {name: '小明',email: 'xiaoming@example.com',age: 25
};postData('https://jsonplaceholder.typicode.com/users', newUser, (error, response) => {if (error) {console.error('创建用户失败:', error.message);} else {console.log('创建成功:', response);}
});

封装成通用函数

function ajax(options) {// 默认设置const defaults = {method: 'GET',url: '',data: null,headers: {},timeout: 5000,success: function() {},error: function() {}};// 合并配置const config = { ...defaults, ...options };const xhr = new XMLHttpRequest();// 设置超时xhr.timeout = config.timeout;xhr.open(config.method, config.url, true);// 设置请求头for (let key in config.headers) {xhr.setRequestHeader(key, config.headers[key]);}// 如果是 POST 请求且有数据,设置默认 Content-Typeif (config.method === 'POST' && config.data && !config.headers['Content-Type']) {xhr.setRequestHeader('Content-Type', 'application/json');}xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status >= 200 && xhr.status < 300) {let response = xhr.responseText;try {response = JSON.parse(response);} catch (e) {// 如果不是 JSON,保持原样}config.success(response);} else {config.error(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));}}};xhr.ontimeout = function() {config.error(new Error('请求超时'));};// 发送数据let sendData = config.data;if (sendData && typeof sendData === 'object') {sendData = JSON.stringify(sendData);}xhr.send(sendData);
}// 使用示例
ajax({method: 'GET',url: 'https://jsonplaceholder.typicode.com/posts/1',success: function(data) {console.log('获取成功:', data);},error: function(error) {console.error('请求失败:', error.message);}
});

Fetch API (现代方式)

为什么用 Fetch?

Fetch 是现代浏览器提供的新 API,比 XMLHttpRequest 更简洁、更强大!

优势:

  • ✅ 基于 Promise,支持 async/await
  • ✅ 语法更简洁
  • ✅ 更好的错误处理
  • ✅ 支持流式数据

基础 GET 请求

// 基础用法
fetch('https://jsonplaceholder.typicode.com/posts/1').then(response => response.json()).then(data => console.log(data)).catch(error => console.error('出错了:', error));// 使用 async/await(推荐)
async function fetchData() {try {const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');const data = await response.json();console.log(data);} catch (error) {console.error('出错了:', error);}
}fetchData();

POST 请求

async function createPost(postData) {try {const response = await fetch('https://jsonplaceholder.typicode.com/posts', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify(postData)});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const result = await response.json();console.log('创建成功:', result);return result;} catch (error) {console.error('创建失败:', error);}
}// 使用示例
createPost({title: '我的第一篇博客',body: '这是内容...',userId: 1
});

常用的 HTTP 方法

class ApiService {constructor(baseURL) {this.baseURL = baseURL;}// GET - 获取数据async get(endpoint) {const response = await fetch(`${this.baseURL}${endpoint}`);return this.handleResponse(response);}// POST - 创建数据async post(endpoint, data) {const response = await fetch(`${this.baseURL}${endpoint}`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(data)});return this.handleResponse(response);}// PUT - 更新数据async put(endpoint, data) {const response = await fetch(`${this.baseURL}${endpoint}`, {method: 'PUT',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(data)});return this.handleResponse(response);}// DELETE - 删除数据async delete(endpoint) {const response = await fetch(`${this.baseURL}${endpoint}`, {method: 'DELETE'});return this.handleResponse(response);}// 统一处理响应async handleResponse(response) {if (!response.ok) {throw new Error(`HTTP ${response.status}: ${response.statusText}`);}// 如果没有内容,返回 nullif (response.status === 204) {return null;}return await response.json();}
}// 使用示例
const api = new ApiService('https://jsonplaceholder.typicode.com');async function example() {try {// 获取所有文章const posts = await api.get('/posts');console.log('所有文章:', posts);// 创建新文章const newPost = await api.post('/posts', {title: '新文章',body: '文章内容',userId: 1});console.log('新文章:', newPost);// 更新文章const updatedPost = await api.put('/posts/1', {id: 1,title: '更新的标题',body: '更新的内容',userId: 1});console.log('更新后:', updatedPost);// 删除文章await api.delete('/posts/1');console.log('删除成功');} catch (error) {console.error('操作失败:', error.message);}
}

处理不同数据格式

JSON 数据(最常用)

// 发送 JSON
async function sendJSON(data) {const response = await fetch('/api/users', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(data)});return await response.json();
}// 接收 JSON
async function receiveJSON() {const response = await fetch('/api/users');const data = await response.json();return data;
}

表单数据

// 发送表单数据
async function sendFormData(formElement) {const formData = new FormData(formElement);const response = await fetch('/api/upload', {method: 'POST',body: formData // 注意:不要设置 Content-Type,让浏览器自动设置});return await response.json();
}// 手动创建表单数据
async function sendCustomFormData() {const formData = new FormData();formData.append('name', '小明');formData.append('age', '25');formData.append('avatar', fileInput.files[0]); // 文件const response = await fetch('/api/profile', {method: 'POST',body: formData});return await response.json();
}

文件上传

// 单文件上传
async function uploadFile(file) {const formData = new FormData();formData.append('file', file);try {const response = await fetch('/api/upload', {method: 'POST',body: formData});if (!response.ok) {throw new Error('上传失败');}const result = await response.json();console.log('上传成功:', result);return result;} catch (error) {console.error('上传出错:', error);}
}// 多文件上传
async function uploadMultipleFiles(files) {const formData = new FormData();for (let i = 0; i < files.length; i++) {formData.append('files', files[i]);}const response = await fetch('/api/upload-multiple', {method: 'POST',body: formData});return await response.json();
}// 带进度的文件上传(使用 XMLHttpRequest)
function uploadWithProgress(file, onProgress) {return new Promise((resolve, reject) => {const formData = new FormData();formData.append('file', file);const xhr = new XMLHttpRequest();// 上传进度xhr.upload.addEventListener('progress', (e) => {if (e.lengthComputable) {const percentComplete = (e.loaded / e.total) * 100;onProgress(percentComplete);}});xhr.addEventListener('load', () => {if (xhr.status === 200) {resolve(JSON.parse(xhr.responseText));} else {reject(new Error('上传失败'));}});xhr.addEventListener('error', () => {reject(new Error('网络错误'));});xhr.open('POST', '/api/upload');xhr.send(formData);});
}// 使用示例
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (e) => {const file = e.target.files[0];if (file) {try {const result = await uploadWithProgress(file, (progress) => {console.log(`上传进度:${progress.toFixed(1)}%`);});console.log('上传完成:', result);} catch (error) {console.error('上传失败:', error);}}
});

处理其他格式

// 获取纯文本
async function getText() {const response = await fetch('/api/readme.txt');const text = await response.text();return text;
}// 获取二进制数据
async function getBinaryData() {const response = await fetch('/api/image.jpg');const blob = await response.blob();return blob;
}// 获取数组缓冲区
async function getArrayBuffer() {const response = await fetch('/api/data.bin');const buffer = await response.arrayBuffer();return buffer;
}

错误处理和状态码

HTTP 状态码详解

// 常见状态码及其含义
const statusCodes = {200: '成功',201: '创建成功',204: '删除成功(无内容)',400: '请求参数错误',401: '未授权',403: '禁止访问',404: '资源不存在',500: '服务器内部错误',502: '网关错误',503: '服务不可用'
};function getStatusMessage(status) {return statusCodes[status] || '未知错误';
}

完善的错误处理

class ApiError extends Error {constructor(status, message, data = null) {super(message);this.name = 'ApiError';this.status = status;this.data = data;}
}async function safeFetch(url, options = {}) {try {const response = await fetch(url, options);// 检查响应状态if (!response.ok) {let errorData = null;try {errorData = await response.json();} catch (e) {// 如果响应不是 JSON,忽略}throw new ApiError(response.status,errorData?.message || `HTTP ${response.status}: ${response.statusText}`,errorData);}// 根据 Content-Type 自动解析const contentType = response.headers.get('content-type');if (contentType && contentType.includes('application/json')) {return await response.json();} else if (contentType && contentType.includes('text/')) {return await response.text();} else {return await response.blob();}} catch (error) {if (error instanceof ApiError) {throw error;}// 网络错误if (error.name === 'TypeError') {throw new ApiError(0, '网络连接失败,请检查网络设置');}// 其他错误throw new ApiError(0, error.message);}
}// 使用示例
async function handleApiCall() {try {const data = await safeFetch('/api/users/123');console.log('获取成功:', data);} catch (error) {if (error instanceof ApiError) {switch (error.status) {case 401:console.error('请先登录');// 跳转到登录页break;case 403:console.error('权限不足');break;case 404:console.error('用户不存在');break;case 0:console.error('网络错误:', error.message);break;default:console.error('请求失败:', error.message);}} else {console.error('未知错误:', error);}}
}

重试机制

async function fetchWithRetry(url, options = {}, maxRetries = 3) {let lastError;for (let i = 0; i <= maxRetries; i++) {try {const response = await fetch(url, options);// 如果是服务器错误且还有重试次数,继续重试if (response.status >= 500 && i < maxRetries) {throw new Error(`服务器错误 ${response.status}`);}if (!response.ok) {throw new Error(`HTTP ${response.status}`);}return await response.json();} catch (error) {lastError = error;if (i < maxRetries) {console.log(`${i + 1} 次请求失败,${1}秒后重试...`);await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 指数退避}}}throw lastError;
}// 使用示例
async function robustApiCall() {try {const data = await fetchWithRetry('/api/unstable-endpoint');console.log('最终成功:', data);} catch (error) {console.error('重试后仍然失败:', error.message);}
}

Ajax 高级技巧

取消请求

// 使用 AbortController 取消请求
function cancelableRequest(url) {const controller = new AbortController();const promise = fetch(url, {signal: controller.signal}).then(response => response.json());// 返回 promise 和取消函数return {promise,cancel: () => controller.abort()};
}// 使用示例
const { promise, cancel } = cancelableRequest('/api/slow-endpoint');// 5秒后自动取消
setTimeout(() => {cancel();console.log('请求已取消');
}, 5000);promise.then(data => console.log('获取成功:', data)).catch(error => {if (error.name === 'AbortError') {console.log('请求被取消了');} else {console.error('请求失败:', error);}});

请求缓存

class ApiCache {constructor(expireTime = 5 * 60 * 1000) { // 默认5分钟过期this.cache = new Map();this.expireTime = expireTime;}// 生成缓存键getCacheKey(url, options = {}) {return JSON.stringify({ url, ...options });}// 获取缓存get(key) {const item = this.cache.get(key);if (!item) return null;// 检查是否过期if (Date.now() > item.expireAt) {this.cache.delete(key);return null;}return item.data;}// 设置缓存set(key, data) {this.cache.set(key, {data,expireAt: Date.now() + this.expireTime});}// 带缓存的请求async fetch(url, options = {}) {const cacheKey = this.getCacheKey(url, options);// 先查缓存const cached = this.get(cacheKey);if (cached) {console.log('使用缓存数据');return cached;}// 发起请求const response = await fetch(url, options);if (!response.ok) {throw new Error(`HTTP ${response.status}`);}const data = await response.json();// 缓存结果this.set(cacheKey, data);return data;}// 清除缓存clear() {this.cache.clear();}
}// 使用示例
const apiCache = new ApiCache();async function getCachedData() {try {const data = await apiCache.fetch('/api/users');console.log('数据:', data);} catch (error) {console.error('获取失败:', error);}
}// 第一次调用会发起请求
getCachedData();// 第二次调用会使用缓存
setTimeout(() => {getCachedData(); // 使用缓存
}, 1000);

并发控制

class RequestQueue {constructor(maxConcurrent = 3) {this.maxConcurrent = maxConcurrent;this.running = 0;this.queue = [];}async add(requestFn) {return new Promise((resolve, reject) => {this.queue.push({requestFn,resolve,reject});this.process();});}async process() {if (this.running >= this.maxConcurrent || this.queue.length === 0) {return;}this.running++;const { requestFn, resolve, reject } = this.queue.shift();try {const result = await requestFn();resolve(result);} catch (error) {reject(error);} finally {this.running--;this.process(); // 处理下一个请求}}
}// 使用示例
const requestQueue = new RequestQueue(2); // 最多同时2个请求async function batchRequests() {const urls = ['/api/user/1','/api/user/2','/api/user/3','/api/user/4','/api/user/5'];const promises = urls.map(url => requestQueue.add(() => fetch(url).then(r => r.json())));try {const results = await Promise.all(promises);console.log('所有请求完成:', results);} catch (error) {console.error('批量请求失败:', error);}
}batchRequests();

实战项目案例

用户管理系统

class UserManager {constructor() {this.baseURL = '/api/users';this.users = [];this.init();}async init() {await this.loadUsers();this.bindEvents();}// 加载用户列表async loadUsers() {try {const response = await fetch(this.baseURL);this.users = await response.json();this.renderUsers();} catch (error) {this.showError('加载用户失败:' + error.message);}}// 创建用户async createUser(userData) {try {const response = await fetch(this.baseURL, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(userData)});if (!response.ok) {throw new Error('创建失败');}const newUser = await response.json();this.users.push(newUser);this.renderUsers();this.showSuccess('用户创建成功!');} catch (error) {this.showError('创建用户失败:' + error.message);}}// 更新用户async updateUser(id, userData) {try {const response = await fetch(`${this.baseURL}/${id}`, {method: 'PUT',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(userData)});if (!response.ok) {throw new Error('更新失败');}const updatedUser = await response.json();const index = this.users.findIndex(u => u.id === id);if (index !== -1) {this.users[index] = updatedUser;this.renderUsers();this.showSuccess('用户更新成功!');}} catch (error) {this.showError('更新用户失败:' + error.message);}}// 删除用户async deleteUser(id) {if (!confirm('确定要删除这个用户吗?')) {return;}try {const response = await fetch(`${this.baseURL}/${id}`, {method: 'DELETE'});if (!response.ok) {throw new Error('删除失败');}this.users = this.users.filter(u => u.id !== id);this.renderUsers();this.showSuccess('用户删除成功!');} catch (error) {this.showError('删除用户失败:' + error.message);}}// 渲染用户列表renderUsers() {const container = document.getElementById('userList');container.innerHTML = this.users.map(user => `<div class="user-item" data-id="${user.id}"><h3>${user.name}</h3><p>邮箱:${user.email}</p><p>年龄:${user.age}</p><button onclick="userManager.editUser(${user.id})">编辑</button><button onclick="userManager.deleteUser(${user.id})">删除</button></div>`).join('');}// 绑定事件bindEvents() {// 创建用户表单document.getElementById('createForm').addEventListener('submit', async (e) => {e.preventDefault();const formData = new FormData(e.target);const userData = {name: formData.get('name'),email: formData.get('email'),age: parseInt(formData.get('age'))};await this.createUser(userData);e.target.reset();});}// 编辑用户editUser(id) {const user = this.users.find(u => u.id === id);if (user) {document.getElementById('editName').value = user.name;document.getElementById('editEmail').value = user.email;document.getElementById('editAge').value = user.age;document.getElementById('editModal').style.display = 'block';document.getElementById('editForm').onsubmit = async (e) => {e.preventDefault();const formData = new FormData(e.target);const userData = {name: formData.get('name'),email: formData.get('email'),age: parseInt(formData.get('age'))};await this.updateUser(id, userData);document.getElementById('editModal').style.display = 'none';};}}// 显示成功消息showSuccess(message) {this.showMessage(message, 'success');}// 显示错误消息showError(message) {this.showMessage(message, 'error');}// 显示消息showMessage(message, type) {const messageDiv = document.createElement('div');messageDiv.className = `message ${type}`;messageDiv.textContent = message;document.body.appendChild(messageDiv);setTimeout(() => {messageDiv.remove();}, 3000);}
}// 初始化
const userManager = new UserManager();

搜索建议功能

class SearchSuggestion {constructor(inputId, suggestionsId) {this.input = document.getElementById(inputId);this.suggestionsContainer = document.getElementById(suggestionsId);this.cache = new Map();this.currentRequest = null;this.debounceTimer = null;this.init();}init() {this.input.addEventListener('input', (e) => {this.debounceSearch(e.target.value);});this.input.addEventListener('keydown', (e) => {this.handleKeyboard(e);});// 点击外部关闭建议document.addEventListener('click', (e) => {if (!this.input.contains(e.target) && !this.suggestionsContainer.contains(e.target)) {this.hideSuggestions();}});}// 防抖搜索debounceSearch(query) {clearTimeout(this.debounceTimer);this.debounceTimer = setTimeout(() => {this.search(query);}, 300);}async search(query) {if (!query.trim()) {this.hideSuggestions();return;}// 取消之前的请求if (this.currentRequest) {this.currentRequest.abort();}// 检查缓存if (this.cache.has(query)) {this.showSuggestions(this.cache.get(query));return;}try {const controller = new AbortController();this.currentRequest = controller;const response = await fetch(`/api/search/suggestions?q=${encodeURIComponent(query)}`, {signal: controller.signal});if (!response.ok) {throw new Error('搜索失败');}const suggestions = await response.json();// 缓存结果this.cache.set(query, suggestions);this.showSuggestions(suggestions);} catch (error) {if (error.name !== 'AbortError') {console.error('搜索出错:', error);}} finally {this.currentRequest = null;}}showSuggestions(suggestions) {if (suggestions.length === 0) {this.hideSuggestions();return;}const html = suggestions.map((item, index) => `<div class="suggestion-item" data-index="${index}" onclick="searchSuggestion.selectSuggestion('${item.text}')"><span class="suggestion-text">${this.highlightQuery(item.text)}</span><span class="suggestion-count">${item.count} 个结果</span></div>`).join('');this.suggestionsContainer.innerHTML = html;this.suggestionsContainer.style.display = 'block';}hideSuggestions() {this.suggestionsContainer.style.display = 'none';}selectSuggestion(text) {this.input.value = text;this.hideSuggestions();this.performSearch(text);}highlightQuery(text) {const query = this.input.value.trim();if (!query) return text;const regex = new RegExp(`(${query})`, 'gi');return text.replace(regex, '<mark>$1</mark>');}handleKeyboard(e) {const items = this.suggestionsContainer.querySelectorAll('.suggestion-item');const current = this.suggestionsContainer.querySelector('.suggestion-item.active');switch (e.key) {case 'ArrowDown':e.preventDefault();if (current) {current.classList.remove('active');const next = current.nextElementSibling || items[0];next.classList.add('active');} else if (items.length > 0) {items[0].classList.add('active');}break;case 'ArrowUp':e.preventDefault();if (current) {current.classList.remove('active');const prev = current.previousElementSibling || items[items.length - 1];prev.classList.add('active');} else if (items.length > 0) {items[items.length - 1].classList.add('active');}break;case 'Enter':e.preventDefault();if (current) {const text = current.querySelector('.suggestion-text').textContent;this.selectSuggestion(text);} else {this.performSearch(this.input.value);}break;case 'Escape':this.hideSuggestions();break;}}performSearch(query) {console.log('执行搜索:', query);// 这里实现实际的搜索逻辑}
}// 初始化搜索建议
const searchSuggestion = new SearchSuggestion('searchInput', 'suggestions');

最佳实践

1. 安全性考虑

// CSRF 防护
function getCSRFToken() {return document.querySelector('meta[name="csrf-token"]').getAttribute('content');
}// 带 CSRF 令牌的请求
async function secureRequest(url, options = {}) {const defaultHeaders = {'X-CSRF-TOKEN': getCSRFToken(),'Content-Type': 'application/json'};return fetch(url, {...options,headers: {...defaultHeaders,...options.headers}});
}// 验证响应数据
function validateResponse(data, schema) {// 简单的数据验证示例for (let key in schema) {if (schema[key].required && !data.hasOwnProperty(key)) {throw new Error(`缺少必需字段:${key}`);}if (data[key] && schema[key].type && typeof data[key] !== schema[key].type) {throw new Error(`字段类型错误:${key}`);}}return true;
}

2. 性能优化

// 请求去重
class RequestDeduplicator {constructor() {this.pendingRequests = new Map();}async request(key, requestFn) {if (this.pendingRequests.has(key)) {return this.pendingRequests.get(key);}const promise = requestFn().finally(() => {this.pendingRequests.delete(key);});this.pendingRequests.set(key, promise);return promise;}
}const deduplicator = new RequestDeduplicator();// 使用示例
async function getUser(id) {return deduplicator.request(`user-${id}`, () => fetch(`/api/users/${id}`).then(r => r.json()));
}// 多次调用只会发起一次请求
getUser(1);
getUser(1);
getUser(1);

3. 错误监控

// 全局错误监控
class ErrorMonitor {constructor() {this.errors = [];this.maxErrors = 100;}log(error, context = {}) {const errorInfo = {message: error.message,stack: error.stack,timestamp: new Date().toISOString(),url: window.location.href,userAgent: navigator.userAgent,context};this.errors.push(errorInfo);// 保持错误日志数量if (this.errors.length > this.maxErrors) {this.errors.shift();}// 上报错误(可选)this.reportError(errorInfo);}async reportError(errorInfo) {try {await fetch('/api/errors', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(errorInfo)});} catch (e) {console.error('错误上报失败:', e);}}getErrors() {return this.errors;}
}const errorMonitor = new ErrorMonitor();// 包装 fetch 以监控错误
async function monitoredFetch(url, options = {}) {try {const response = await fetch(url, options);if (!response.ok) {const error = new Error(`HTTP ${response.status}: ${response.statusText}`);errorMonitor.log(error, { url, options, status: response.status });throw error;}return response;} catch (error) {errorMonitor.log(error, { url, options });throw error;}
}

4. 通用工具函数

// Ajax 工具库
const AjaxUtils = {// 构建查询字符串buildQuery(params) {return new URLSearchParams(params).toString();},// 解析响应头parseHeaders(response) {const headers = {};for (let [key, value] of response.headers.entries()) {headers[key] = value;}return headers;},// 检查网络状态isOnline() {return navigator.onLine;},// 等待网络恢复waitForOnline() {return new Promise(resolve => {if (this.isOnline()) {resolve();} else {const handler = () => {if (this.isOnline()) {window.removeEventListener('online', handler);resolve();}};window.addEventListener('online', handler);}});},// 格式化文件大小formatFileSize(bytes) {if (bytes === 0) return '0 Bytes';const k = 1024;const sizes = ['Bytes', 'KB', 'MB', 'GB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];}
};

总结

Ajax 学习路径

  1. 掌握基础 - XMLHttpRequest 和 Fetch API
  2. 理解概念 - 同步、异步、状态码、错误处理
  3. 实践应用 - 表单提交、文件上传、数据获取
  4. 高级技巧 - 缓存、重试、并发控制、请求取消
  5. 项目实战 - 结合实际业务场景

开发建议

  • 优先使用 Fetch API,语法更简洁
  • 总是处理错误,提供友好的用户体验
  • 使用 async/await,代码更易读
  • 实现加载状态,让用户知道正在处理
  • 适当使用缓存,提升性能
  • 考虑网络状况,处理离线场景

调试技巧

// 在浏览器控制台查看网络请求
// 1. 打开开发者工具 (F12)
// 2. 切换到 Network 选项卡
// 3. 重新发起请求,查看详细信息// 使用 console.log 调试
fetch('/api/data').then(response => {console.log('响应状态:', response.status);console.log('响应头:', response.headers);return response.json();}).then(data => {console.log('响应数据:', data);});

记住:Ajax 是现代 Web 开发的核心技术,多练习、多实战才能真正掌握! 🚀

相关文章:

  • [AI绘画]sd学习记录(一)软件安装以及文生图界面初识、提示词写法
  • RabbitMQ实用技巧
  • 自定义事件wpf
  • OpenBayes 一周速览|TransPixeler 实现透明化文本到视频生成;统一图像定制框架 DreamO 上线,一键处理多种图像生成任务
  • 【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
  • 音视频之视频压缩编码的基本原理
  • Java中List的forEach用法详解
  • wpf Behaviors库实现支持多选操作进行后台绑定数据的ListView
  • 案例分享--汽车制动卡钳DIC测量
  • CICD实战(一) -----Jenkins的下载与安装
  • [pdf、epub]300道《软件方法》强化自测题业务建模需求分析共257页(202505更新)
  • Java八股文——集合「List篇」
  • Spring Boot缓存组件Ehcache、Caffeine、Redis、Hazelcast
  • 通过Chain Prompts方式将LLM的能力引入测试平台:正交实验测试用例生成
  • To be or Not to be, That‘s a Token——论文阅读笔记——Beyond the 80/20 Rule和R2R
  • 基础线性代数
  • 阿里云ACP云计算备考笔记 (3)——云存储RDS
  • HSL颜色控制及使用示例(Hue-Saturation-Lightness)
  • Jenkins实现自动化部署Springboot项目到Docker容器(Jenkinsfile)
  • java使用文本相似度检测可以调整阈值
  • 深圳松岗 网站建设/天津关键词优化专家
  • 买网站平台名字吗/怎么提高百度关键词排名
  • 梁山做网站的公司/推广运营公司哪家好
  • 肥西网站建设/促销方案
  • 上市公司网站分析/推广软文是什么
  • 电商网站开发的背景/今日国内重大新闻事件