|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
Ajax(Asynchronous JavaScript and XML)是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。自从2005年被Jesse James Garrett提出以来,Ajax已经成为现代Web开发中不可或缺的技术。它通过在后台与服务器进行少量数据交换,使网页实现异步更新,大大提升了用户体验。
本文将从Ajax的基础配置开始,逐步深入到高级应用,全面解析Ajax对象的各种参数,帮助开发者解决实际开发中遇到的问题,提升代码质量和用户体验。
一、Ajax基础:XMLHttpRequest对象的基本用法
1.1 XMLHttpRequest对象简介
XMLHttpRequest是Ajax技术的核心,它提供了一个API,使得JavaScript可以进行HTTP(S)通信。现代浏览器都支持这个对象,它是实现异步通信的基础。
1.2 创建XMLHttpRequest对象
创建XMLHttpRequest对象非常简单,代码如下:
- // 创建XMLHttpRequest对象
- let xhr = new XMLHttpRequest();
复制代码
1.3 基本请求流程
一个基本的Ajax请求包含以下步骤:
- // 1. 创建XMLHttpRequest对象
- let xhr = new XMLHttpRequest();
- // 2. 配置请求
- xhr.open('GET', 'https://api.example.com/data', true);
- // 3. 设置回调函数
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4 && xhr.status === 200) {
- // 请求成功,处理响应数据
- console.log(xhr.responseText);
- }
- };
- // 4. 发送请求
- xhr.send();
复制代码
1.4 基本参数解析
在上面的代码中,我们使用了几个基本参数:
• open(method, url, async):初始化请求method:HTTP请求方法(GET、POST、PUT、DELETE等)url:请求的URL地址async:是否异步(默认为true)
• method:HTTP请求方法(GET、POST、PUT、DELETE等)
• url:请求的URL地址
• async:是否异步(默认为true)
• onreadystatechange:状态改变时的回调函数readyState:请求状态(0-4)0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪
• readyState:请求状态(0-4)0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪
• 0: 请求未初始化
• 1: 服务器连接已建立
• 2: 请求已接收
• 3: 请求处理中
• 4: 请求已完成,且响应已就绪
• status:HTTP状态码(如200表示成功,404表示未找到等)
open(method, url, async):初始化请求
• method:HTTP请求方法(GET、POST、PUT、DELETE等)
• url:请求的URL地址
• async:是否异步(默认为true)
onreadystatechange:状态改变时的回调函数
• readyState:请求状态(0-4)0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪
• 0: 请求未初始化
• 1: 服务器连接已建立
• 2: 请求已接收
• 3: 请求处理中
• 4: 请求已完成,且响应已就绪
• 0: 请求未初始化
• 1: 服务器连接已建立
• 2: 请求已接收
• 3: 请求处理中
• 4: 请求已完成,且响应已就绪
status:HTTP状态码(如200表示成功,404表示未找到等)
二、Ajax进阶:详细的参数配置和选项
2.1 请求方法详解
不同的HTTP方法适用于不同的场景:
- // GET请求:获取数据
- let xhrGet = new XMLHttpRequest();
- xhrGet.open('GET', 'https://api.example.com/users?id=123', true);
- xhrGet.send();
- // POST请求:提交数据
- let xhrPost = new XMLHttpRequest();
- xhrPost.open('POST', 'https://api.example.com/users', true);
- xhrPost.setRequestHeader('Content-Type', 'application/json');
- xhrPost.send(JSON.stringify({name: 'John', age: 30}));
- // PUT请求:更新数据
- let xhrPut = new XMLHttpRequest();
- xhrPut.open('PUT', 'https://api.example.com/users/123', true);
- xhrPut.setRequestHeader('Content-Type', 'application/json');
- xhrPut.send(JSON.stringify({name: 'John Updated', age: 31}));
- // DELETE请求:删除数据
- let xhrDelete = new XMLHttpRequest();
- xhrDelete.open('DELETE', 'https://api.example.com/users/123', true);
- xhrDelete.send();
复制代码
2.2 请求头设置
通过setRequestHeader方法可以设置请求头,这对于控制请求行为非常重要:
- let xhr = new XMLHttpRequest();
- xhr.open('POST', 'https://api.example.com/data', true);
- // 设置Content-Type
- xhr.setRequestHeader('Content-Type', 'application/json');
- // 设置授权信息
- xhr.setRequestHeader('Authorization', 'Bearer your_token_here');
- // 设置自定义请求头
- xhr.setRequestHeader('X-Custom-Header', 'custom_value');
- // 发送JSON数据
- xhr.send(JSON.stringify({key: 'value'}));
复制代码
常见的请求头包括:
• Content-Type:请求体的内容类型application/json:JSON数据application/x-www-form-urlencoded:表单数据multipart/form-data:文件上传text/plain:纯文本
• application/json:JSON数据
• application/x-www-form-urlencoded:表单数据
• multipart/form-data:文件上传
• text/plain:纯文本
• Authorization:认证信息Bearer token:Bearer令牌认证Basic credentials:基本认证
• Bearer token:Bearer令牌认证
• Basic credentials:基本认证
• Accept:可接受的响应内容类型
• Cache-Control:缓存控制
• X-Requested-With:常用于标识Ajax请求(值为XMLHttpRequest)
Content-Type:请求体的内容类型
• application/json:JSON数据
• application/x-www-form-urlencoded:表单数据
• multipart/form-data:文件上传
• text/plain:纯文本
Authorization:认证信息
• Bearer token:Bearer令牌认证
• Basic credentials:基本认证
Accept:可接受的响应内容类型
Cache-Control:缓存控制
X-Requested-With:常用于标识Ajax请求(值为XMLHttpRequest)
2.3 响应处理
处理响应数据是Ajax请求的重要部分:
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data', true);
- xhr.onreadystatechange = function() {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- // 请求成功
- console.log('Response Text:', xhr.responseText);
- console.log('Response XML:', xhr.responseXML);
- console.log('Response JSON:', JSON.parse(xhr.responseText));
- } else {
- // 请求失败
- console.error('Request failed with status:', xhr.status);
- }
- }
- };
- xhr.send();
复制代码
除了onreadystatechange,我们还可以使用其他事件处理程序:
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data', true);
- // 加载完成事件
- xhr.onload = function() {
- if (xhr.status === 200) {
- console.log('Request successful');
- } else {
- console.error('Request failed with status:', xhr.status);
- }
- };
- // 错误事件
- xhr.onerror = function() {
- console.error('Request failed due to a network error');
- };
- // 进度事件(用于上传和下载)
- xhr.onprogress = function(event) {
- if (event.lengthComputable) {
- const percentComplete = (event.loaded / event.total) * 100;
- console.log('Progress:', percentComplete + '%');
- }
- };
- xhr.send();
复制代码
2.4 超时设置
为避免请求长时间无响应,可以设置超时时间:
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data', true);
- // 设置超时时间为5000毫秒(5秒)
- xhr.timeout = 5000;
- // 超时事件处理
- xhr.ontimeout = function() {
- console.error('Request timed out');
- };
- xhr.send();
复制代码
2.5 跨域请求
跨域请求是Ajax开发中常见的问题,可以通过CORS(跨源资源共享)或JSONP(仅支持GET请求)解决:
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data', true);
- // 现代浏览器会自动处理CORS,但服务器需要设置适当的响应头
- // 如:Access-Control-Allow-Origin: *
- xhr.withCredentials = true; // 是否发送凭证(如cookies)
- xhr.onload = function() {
- if (xhr.status === 200) {
- console.log(xhr.responseText);
- }
- };
- xhr.send();
复制代码- function jsonpRequest(url, callback) {
- // 创建一个唯一的回调函数名
- const callbackName = 'jsonp_callback_' + Date.now();
-
- // 将回调函数添加到全局作用域
- window[callbackName] = function(data) {
- delete window[callbackName];
- document.body.removeChild(script);
- callback(data);
- };
-
- // 创建script标签
- const script = document.createElement('script');
- script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
- document.body.appendChild(script);
- }
- // 使用JSONP请求数据
- jsonpRequest('https://api.example.com/data', function(data) {
- console.log('JSONP response:', data);
- });
复制代码
三、高级应用:Promise、Fetch API与Ajax的结合
3.1 封装XMLHttpRequest为Promise
为了更好地处理异步操作,我们可以将XMLHttpRequest封装为Promise:
- function ajaxPromise(options) {
- return new Promise((resolve, reject) => {
- const xhr = new XMLHttpRequest();
-
- // 初始化请求
- xhr.open(options.method || 'GET', options.url, true);
-
- // 设置请求头
- if (options.headers) {
- for (const key in options.headers) {
- if (options.headers.hasOwnProperty(key)) {
- xhr.setRequestHeader(key, options.headers[key]);
- }
- }
- }
-
- // 设置超时
- if (options.timeout) {
- xhr.timeout = options.timeout;
- }
-
- // 设置响应类型
- if (options.responseType) {
- xhr.responseType = options.responseType;
- }
-
- // 处理加载完成
- xhr.onload = function() {
- if (xhr.status >= 200 && xhr.status < 300) {
- resolve(xhr.response);
- } else {
- reject({
- status: xhr.status,
- statusText: xhr.statusText,
- response: xhr.response
- });
- }
- };
-
- // 处理错误
- xhr.onerror = function() {
- reject({
- status: xhr.status,
- statusText: xhr.statusText,
- response: xhr.response
- });
- };
-
- // 处理超时
- xhr.ontimeout = function() {
- reject({
- status: 0,
- statusText: 'Request timed out',
- response: null
- });
- };
-
- // 发送请求
- if (options.data) {
- xhr.send(options.data);
- } else {
- xhr.send();
- }
- });
- }
- // 使用Promise封装的Ajax
- ajaxPromise({
- method: 'GET',
- url: 'https://api.example.com/data',
- headers: {
- 'Content-Type': 'application/json'
- }
- })
- .then(response => {
- console.log('Success:', response);
- })
- .catch(error => {
- console.error('Error:', error);
- });
复制代码
3.2 Fetch API简介
Fetch API是现代浏览器提供的新的网络请求API,它提供了更强大和灵活的功能:
- // 基本Fetch请求
- fetch('https://api.example.com/data')
- .then(response => {
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- return response.json(); // 解析JSON数据
- })
- .then(data => {
- console.log('Success:', data);
- })
- .catch(error => {
- console.error('Error:', error);
- });
- // POST请求
- fetch('https://api.example.com/data', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({key: 'value'})
- })
- .then(response => response.json())
- .then(data => console.log('Success:', data))
- .catch(error => console.error('Error:', error));
复制代码
3.3 Fetch API与XMLHttpRequest的对比
Fetch API相比XMLHttpRequest有以下优势:
1. 语法更简洁,基于Promise设计
2. 更好的流操作
3. 更强大的请求和响应控制
但也有一些不足:
1. 不支持请求进度事件
2. 默认不发送cookies,需要设置credentials选项
3. 对HTTP错误状态码的处理方式不同(不会自动reject)
3.4 使用async/await简化异步代码
使用async/await可以进一步简化异步代码:
- // 使用async/await处理Fetch请求
- async function fetchData() {
- try {
- const response = await fetch('https://api.example.com/data');
-
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
-
- const data = await response.json();
- console.log('Success:', data);
- return data;
- } catch (error) {
- console.error('Error:', error);
- throw error;
- }
- }
- // 调用函数
- fetchData()
- .then(data => {
- // 处理数据
- })
- .catch(error => {
- // 处理错误
- });
复制代码
四、实际问题解决:常见问题和解决方案
4.1 缓存问题
浏览器可能会缓存Ajax请求,导致获取不到最新数据。解决方法:
- // 方法1:添加时间戳参数
- function noCacheUrl(url) {
- return url + (url.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
- }
- let xhr = new XMLHttpRequest();
- xhr.open('GET', noCacheUrl('https://api.example.com/data'), true);
- xhr.send();
- // 方法2:设置请求头
- let xhr2 = new XMLHttpRequest();
- xhr2.open('GET', 'https://api.example.com/data', true);
- xhr2.setRequestHeader('Cache-Control', 'no-cache');
- xhr2.setRequestHeader('Pragma', 'no-cache');
- xhr2.send();
复制代码
4.2 请求取消
取消正在进行的Ajax请求:
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'https://api.example.com/data', true);
- // 设置一个取消按钮
- document.getElementById('cancelButton').addEventListener('click', function() {
- xhr.abort(); // 取消请求
- console.log('Request cancelled');
- });
- xhr.send();
复制代码
使用Fetch API和AbortController取消请求:
- const controller = new AbortController();
- const signal = controller.signal;
- fetch('https://api.example.com/data', { signal })
- .then(response => response.json())
- .then(data => console.log('Success:', data))
- .catch(error => {
- if (error.name === 'AbortError') {
- console.log('Request was aborted');
- } else {
- console.error('Error:', error);
- }
- });
- // 取消请求
- document.getElementById('cancelButton').addEventListener('click', function() {
- controller.abort();
- });
复制代码
4.3 请求重试机制
实现请求重试机制,提高请求成功率:
- function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {
- return new Promise((resolve, reject) => {
- const attempt = (attemptNumber) => {
- fetch(url, options)
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => resolve(data))
- .catch(error => {
- if (attemptNumber < retries) {
- console.log(`Attempt ${attemptNumber} failed. Retrying in ${delay}ms...`);
- setTimeout(() => attempt(attemptNumber + 1), delay);
- } else {
- reject(error);
- }
- });
- };
-
- attempt(1);
- });
- }
- // 使用重试机制
- fetchWithRetry('https://api.example.com/data')
- .then(data => console.log('Success:', data))
- .catch(error => console.error('All attempts failed:', error));
复制代码
4.4 请求队列管理
当需要管理多个Ajax请求时,可以实现请求队列:
- class RequestQueue {
- constructor(maxConcurrent = 3) {
- this.queue = [];
- this.activeRequests = 0;
- this.maxConcurrent = maxConcurrent;
- }
-
- add(requestFn) {
- return new Promise((resolve, reject) => {
- this.queue.push({
- requestFn,
- resolve,
- reject
- });
- this.processQueue();
- });
- }
-
- processQueue() {
- if (this.activeRequests >= this.maxConcurrent || this.queue.length === 0) {
- return;
- }
-
- this.activeRequests++;
-
- const { requestFn, resolve, reject } = this.queue.shift();
-
- requestFn()
- .then(result => {
- resolve(result);
- })
- .catch(error => {
- reject(error);
- })
- .finally(() => {
- this.activeRequests--;
- this.processQueue();
- });
- }
- }
- // 使用请求队列
- const queue = new RequestQueue(2); // 最多同时2个请求
- // 添加多个请求到队列
- for (let i = 1; i <= 5; i++) {
- queue.add(() => fetch(`https://api.example.com/data/${i}`))
- .then(response => response.json())
- .then(data => console.log(`Request ${i} completed:`, data))
- .catch(error => console.error(`Request ${i} failed:`, error));
- }
复制代码
4.5 处理大文件上传
使用Ajax上传大文件时,可以通过分片上传和进度显示来提升用户体验:
- function uploadFile(file, url, chunkSize = 1024 * 1024) { // 默认分片大小为1MB
- return new Promise((resolve, reject) => {
- const fileSize = file.size;
- const chunks = Math.ceil(fileSize / chunkSize);
- let currentChunk = 0;
-
- const uploadChunk = (start, end) => {
- const chunk = file.slice(start, end);
- const xhr = new XMLHttpRequest();
- const formData = new FormData();
-
- formData.append('file', chunk);
- formData.append('fileName', file.name);
- formData.append('fileSize', fileSize);
- formData.append('chunkIndex', currentChunk);
- formData.append('totalChunks', chunks);
-
- xhr.open('POST', url, true);
-
- xhr.onload = function() {
- if (xhr.status === 200) {
- currentChunk++;
-
- if (currentChunk < chunks) {
- // 上传进度
- const progress = Math.round((currentChunk / chunks) * 100);
- console.log(`Upload progress: ${progress}%`);
-
- // 继续上传下一片
- uploadChunk(currentChunk * chunkSize, Math.min((currentChunk + 1) * chunkSize, fileSize));
- } else {
- // 所有分片上传完成
- resolve(JSON.parse(xhr.responseText));
- }
- } else {
- reject(new Error(`Upload failed with status: ${xhr.status}`));
- }
- };
-
- xhr.onerror = function() {
- reject(new Error('Network error occurred during upload'));
- };
-
- xhr.send(formData);
- };
-
- // 开始上传第一片
- uploadChunk(0, Math.min(chunkSize, fileSize));
- });
- }
- // 使用分片上传
- document.getElementById('fileInput').addEventListener('change', function(e) {
- const file = e.target.files[0];
- if (file) {
- uploadFile(file, 'https://api.example.com/upload')
- .then(result => {
- console.log('Upload successful:', result);
- })
- .catch(error => {
- console.error('Upload failed:', error);
- });
- }
- });
复制代码
五、代码质量与用户体验:最佳实践和性能优化
5.1 错误处理与用户反馈
良好的错误处理和用户反馈是提升用户体验的关键:
- function makeRequest(url, options = {}) {
- // 显示加载指示器
- showLoadingIndicator();
-
- return fetch(url, options)
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- })
- .catch(error => {
- // 根据错误类型显示不同的用户反馈
- if (error.message.includes('NetworkError')) {
- showUserMessage('网络连接错误,请检查您的网络设置');
- } else if (error.message.includes('HTTP error! status: 404')) {
- showUserMessage('请求的资源不存在');
- } else if (error.message.includes('HTTP error! status: 500')) {
- showUserMessage('服务器内部错误,请稍后再试');
- } else {
- showUserMessage('请求失败,请稍后再试');
- }
-
- // 记录错误日志
- logError(error);
-
- // 重新抛出错误以便调用者处理
- throw error;
- })
- .finally(() => {
- // 隐藏加载指示器
- hideLoadingIndicator();
- });
- }
- // 辅助函数
- function showLoadingIndicator() {
- // 显示加载指示器的实现
- console.log('Loading...');
- }
- function hideLoadingIndicator() {
- // 隐藏加载指示器的实现
- console.log('Loading complete');
- }
- function showUserMessage(message) {
- // 显示用户消息的实现
- console.log('User message:', message);
- }
- function logError(error) {
- // 记录错误的实现
- console.error('Error logged:', error);
- }
- // 使用改进的请求函数
- makeRequest('https://api.example.com/data')
- .then(data => {
- console.log('Success:', data);
- })
- .catch(error => {
- // 错误已经被处理,这里可以执行额外的错误处理逻辑
- });
复制代码
5.2 请求节流与防抖
在处理频繁触发的事件(如搜索框输入)时,使用节流和防抖可以减少不必要的请求:
- // 防抖函数
- function debounce(func, wait) {
- let timeout;
- return function(...args) {
- const context = this;
- clearTimeout(timeout);
- timeout = setTimeout(() => func.apply(context, args), wait);
- };
- }
- // 节流函数
- function throttle(func, limit) {
- let inThrottle;
- return function(...args) {
- const context = this;
- if (!inThrottle) {
- func.apply(context, args);
- inThrottle = true;
- setTimeout(() => inThrottle = false, limit);
- }
- };
- }
- // 使用防抖处理搜索输入
- const searchInput = document.getElementById('searchInput');
- const searchResults = document.getElementById('searchResults');
- const performSearch = debounce(function(query) {
- if (query.trim() === '') {
- searchResults.innerHTML = '';
- return;
- }
-
- fetch(`https://api.example.com/search?q=${encodeURIComponent(query)}`)
- .then(response => response.json())
- .then(data => {
- displaySearchResults(data);
- })
- .catch(error => {
- console.error('Search error:', error);
- searchResults.innerHTML = '<div class="error">搜索出错,请稍后再试</div>';
- });
- }, 300); // 300毫秒的防抖延迟
- searchInput.addEventListener('input', function(e) {
- performSearch(e.target.value);
- });
- // 显示搜索结果的函数
- function displaySearchResults(results) {
- // 实现显示搜索结果的逻辑
- console.log('Search results:', results);
- }
复制代码
5.3 数据缓存策略
实现数据缓存可以减少不必要的网络请求,提高应用性能:
- class DataCache {
- constructor(expireTime = 60000) { // 默认缓存1分钟
- this.cache = new Map();
- this.expireTime = expireTime;
- }
-
- get(key) {
- const item = this.cache.get(key);
- if (!item) {
- return null;
- }
-
- // 检查是否过期
- const now = Date.now();
- if (now > item.expireTime) {
- this.cache.delete(key);
- return null;
- }
-
- return item.data;
- }
-
- set(key, data, customExpireTime) {
- const expireTime = customExpireTime || (Date.now() + this.expireTime);
- this.cache.set(key, {
- data,
- expireTime
- });
- }
-
- clear() {
- this.cache.clear();
- }
-
- delete(key) {
- this.cache.delete(key);
- }
- }
- // 创建缓存实例
- const apiCache = new DataCache(5 * 60 * 1000); // 缓存5分钟
- // 带缓存的请求函数
- function cachedFetch(url, options = {}) {
- // 检查缓存
- const cachedData = apiCache.get(url);
- if (cachedData) {
- console.log('Data retrieved from cache');
- return Promise.resolve(cachedData);
- }
-
- // 没有缓存,发起请求
- return fetch(url, options)
- .then(response => {
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- })
- .then(data => {
- // 存入缓存
- apiCache.set(url, data);
- return data;
- });
- }
- // 使用带缓存的请求
- cachedFetch('https://api.example.com/data')
- .then(data => {
- console.log('Data:', data);
- })
- .catch(error => {
- console.error('Error:', error);
- });
复制代码
5.4 请求优先级管理
在复杂应用中,可能需要根据优先级管理请求:
- class PriorityRequestQueue {
- constructor() {
- this.queue = {
- high: [],
- medium: [],
- low: []
- };
- this.activeRequests = 0;
- this.maxConcurrent = 3;
- }
-
- add(requestFn, priority = 'medium') {
- return new Promise((resolve, reject) => {
- if (!this.queue[priority]) {
- priority = 'medium';
- }
-
- this.queue[priority].push({
- requestFn,
- resolve,
- reject
- });
-
- this.processQueue();
- });
- }
-
- processQueue() {
- if (this.activeRequests >= this.maxConcurrent) {
- return;
- }
-
- // 按优先级获取下一个请求
- let nextRequest = null;
- if (this.queue.high.length > 0) {
- nextRequest = this.queue.high.shift();
- } else if (this.queue.medium.length > 0) {
- nextRequest = this.queue.medium.shift();
- } else if (this.queue.low.length > 0) {
- nextRequest = this.queue.low.shift();
- }
-
- if (!nextRequest) {
- return;
- }
-
- this.activeRequests++;
-
- const { requestFn, resolve, reject } = nextRequest;
-
- requestFn()
- .then(result => {
- resolve(result);
- })
- .catch(error => {
- reject(error);
- })
- .finally(() => {
- this.activeRequests--;
- this.processQueue();
- });
- }
- }
- // 使用优先级队列
- const priorityQueue = new PriorityRequestQueue();
- // 添加不同优先级的请求
- priorityQueue.add(() => fetch('https://api.example.com/critical-data'), 'high')
- .then(response => response.json())
- .then(data => console.log('High priority request completed:', data));
- priorityQueue.add(() => fetch('https://api.example.com/normal-data'), 'medium')
- .then(response => response.json())
- .then(data => console.log('Medium priority request completed:', data));
- priorityQueue.add(() => fetch('https://api.example.com/background-data'), 'low')
- .then(response => response.json())
- .then(data => console.log('Low priority request completed:', data));
复制代码
5.5 请求拦截器
实现请求拦截器可以在请求发送前和响应接收后统一处理:
- class HttpClient {
- constructor(baseURL = '') {
- this.baseURL = baseURL;
- this.requestInterceptors = [];
- this.responseInterceptors = [];
- }
-
- // 添加请求拦截器
- addRequestInterceptor(interceptor) {
- this.requestInterceptors.push(interceptor);
- }
-
- // 添加响应拦截器
- addResponseInterceptor(interceptor) {
- this.responseInterceptors.push(interceptor);
- }
-
- // 执行请求拦截器
- async runRequestInterceptors(config) {
- for (const interceptor of this.requestInterceptors) {
- config = await interceptor(config);
- }
- return config;
- }
-
- // 执行响应拦截器
- async runResponseInterceptors(response) {
- for (const interceptor of this.responseInterceptors) {
- response = await interceptor(response);
- }
- return response;
- }
-
- // 发起请求
- async request(url, options = {}) {
- // 构建完整URL
- const fullUrl = this.baseURL + url;
-
- // 创建请求配置
- let config = {
- url: fullUrl,
- ...options
- };
-
- // 执行请求拦截器
- config = await this.runRequestInterceptors(config);
-
- // 发起请求
- let response;
- try {
- response = await fetch(config.url, {
- method: config.method || 'GET',
- headers: config.headers || {},
- body: config.body,
- credentials: config.credentials || 'same-origin',
- signal: config.signal
- });
- } catch (error) {
- throw error;
- }
-
- // 执行响应拦截器
- response = await this.runResponseInterceptors(response);
-
- // 检查响应状态
- if (!response.ok) {
- const error = new Error(`HTTP error! status: ${response.status}`);
- error.response = response;
- throw error;
- }
-
- // 根据配置解析响应
- if (config.responseType === 'json') {
- return response.json();
- } else if (config.responseType === 'text') {
- return response.text();
- } else if (config.responseType === 'blob') {
- return response.blob();
- } else {
- return response;
- }
- }
-
- // 便捷方法
- get(url, options = {}) {
- return this.request(url, { ...options, method: 'GET' });
- }
-
- post(url, data, options = {}) {
- const headers = {
- 'Content-Type': 'application/json',
- ...(options.headers || {})
- };
-
- return this.request(url, {
- ...options,
- method: 'POST',
- headers,
- body: JSON.stringify(data),
- responseType: 'json'
- });
- }
-
- put(url, data, options = {}) {
- const headers = {
- 'Content-Type': 'application/json',
- ...(options.headers || {})
- };
-
- return this.request(url, {
- ...options,
- method: 'PUT',
- headers,
- body: JSON.stringify(data),
- responseType: 'json'
- });
- }
-
- delete(url, options = {}) {
- return this.request(url, { ...options, method: 'DELETE' });
- }
- }
- // 使用HTTP客户端
- const httpClient = new HttpClient('https://api.example.com');
- // 添加请求拦截器 - 添加认证token
- httpClient.addRequestInterceptor(async (config) => {
- const token = localStorage.getItem('authToken');
- if (token) {
- config.headers = {
- ...config.headers,
- 'Authorization': `Bearer ${token}`
- };
- }
- return config;
- });
- // 添加请求拦截器 - 添加时间戳防止缓存
- httpClient.addRequestInterceptor(async (config) => {
- if (config.method === 'GET') {
- const separator = config.url.includes('?') ? '&' : '?';
- config.url = `${config.url}${separator}_t=${Date.now()}`;
- }
- return config;
- });
- // 添加响应拦截器 - 统一错误处理
- httpClient.addResponseInterceptor(async (response) => {
- if (response.status === 401) {
- // 未授权,跳转到登录页
- window.location.href = '/login';
- }
- return response;
- });
- // 使用HTTP客户端发起请求
- httpClient.get('/users')
- .then(data => {
- console.log('Users:', data);
- })
- .catch(error => {
- console.error('Error:', error);
- });
- httpClient.post('/users', { name: 'John', email: 'john@example.com' })
- .then(data => {
- console.log('Created user:', data);
- })
- .catch(error => {
- console.error('Error:', error);
- });
复制代码
六、总结
本文详细介绍了Ajax对象的各种参数配置,从基础的XMLHttpRequest对象到高级的Fetch API,从简单的请求到复杂的队列管理和缓存策略。通过掌握这些知识,开发者可以:
1. 灵活配置Ajax请求,满足不同场景的需求
2. 处理常见的Ajax问题,如缓存、跨域、请求取消等
3. 实现高级功能,如请求重试、分片上传、优先级管理等
4. 提升代码质量,通过封装、拦截器等技术使代码更易维护
5. 优化用户体验,通过加载指示器、错误处理、进度显示等提升交互体验
随着Web技术的发展,Ajax技术也在不断演进。从最初的XMLHttpRequest到现代的Fetch API,从回调函数到Promise再到async/await,异步编程变得更加简洁和强大。未来,随着Web标准和浏览器的进一步发展,我们有理由相信,Web应用中的异步通信将变得更加高效和易用。
无论技术如何变化,理解Ajax的基本原理和最佳实践,掌握异步编程的核心思想,对于Web开发者来说都是必不可少的技能。希望本文能帮助读者全面掌握Ajax技术,在实际开发中构建出更加优秀的Web应用。 |
|