Ajax、Fetch、Axios的区别和使用方法

221 阅读7分钟

1. 概念和历史背景

  • AJAX(Asynchronous JavaScript and XML)

    • 是一种在不刷新整个页面的情况下,与服务器进行异步通信并更新部分网页的技术。它并非单一的技术,而是多种技术的组合,包括 XMLHttpRequest 对象、JavaScript、HTML 和 CSS 等。
    • 诞生较早,在 2005 年左右开始流行,当时主要用于在网页中实现动态数据加载,打破了传统网页需要刷新整个页面才能获取新数据的局限。最初使用 XML 格式传输数据,但现在更多使用 JSON 格式。
  • fetch

    • 是现代浏览器提供的一个用于发起网络请求的 API,基于 Promise 实现,是 XMLHttpRequest 的一种替代方案。
    • 随着 JavaScript 语言的发展,为了提供更简洁、更强大的网络请求功能而推出,是 Web API 的一部分,在较新的浏览器中得到了广泛支持。
  • axios

    • 是一个基于 Promise 的 HTTP 客户端,既可以在浏览器环境中使用,也可以在 Node.js 环境中使用。
    • 是一个第三方库,由开发者社区维护和开发,旨在提供更便捷、更强大的 HTTP 请求功能,弥补了 fetch 和 XMLHttpRequest 在某些方面的不足。
相同点
// 请求都可被取消
// - xhr
xhr.abort()  readyState变为0

// - fetch
   const controller = new AbortController()
   const signal = controller.signal
   fetch(url, {signal}).then(() => ...).catch(err => if (err.name === 'AbortError'){console.log('cancel', err.message)})
   controller.abort()

// - axios
   const cancelToken = axios.CancelToken
   let cancel
   axios.get(url, {
     cancelToken: new CancelToken(function(c){
       cancel = c
     })
   }).then().catch((err) => {if (axios.isCancel(err)) {console.log('cancel', err.message)}})
    
    cancel('user cancel')
不同点

1.xhr、fetch在get请求下参数需要手动拼接到url上(当然也可以使用URLSearchParams处理)

// 定义请求的基础 URL
const baseUrl = 'https://jsonplaceholder.typicode.com/posts';
// 定义参数对象
const params = {
  userId: 1,
  _sort: 'id',
  _order: 'desc'
};

const searchParams = new URLSearchParams(params)

// 拼接完整的 URL
const url = `${baseUrl}?${searchParams.toString()}`;

2.xhr、fetch不带拦截器

1. 原生 JavaScript 实现 AJAX

使用原生 JavaScript 实现 AJAX 请求,主要借助 XMLHttpRequest 对象,以下是详细步骤和示例代码:

步骤

  1. 创建 XMLHttpRequest 对象:这是发起 AJAX 请求的核心对象。
  2. 打开连接:使用 open() 方法指定请求方法(如 GET、POST)、请求的 URL 以及是否异步等信息。
  3. 发送请求:使用 send() 方法发送请求,如果是 POST 请求,还需要设置请求头并传递请求体数据。
  4. 监听状态变化:通过监听 onreadystatechange 事件,获取请求的状态变化,当状态码为 200 且请求完成时,说明请求成功。
  5. 处理响应:根据响应的状态和内容进行相应的处理。

示例代码(GET 请求)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>原生 AJAX GET 请求示例</title>
</head>

<body>
    <button id="fetchDataButton">获取数据</button>
    <div id="result"></div>

    <script>
        const fetchDataButton = document.getElementById('fetchDataButton');
        const resultDiv = document.getElementById('result');

        fetchDataButton.addEventListener('click', () => {
            // 创建 XMLHttpRequest 对象
            const xhr = new XMLHttpRequest();

            // 打开连接
            xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1', true);

            // 监听状态变化
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    // 请求成功,处理响应数据
                    const responseData = JSON.parse(xhr.responseText);
                    resultDiv.innerHTML = `<p>标题: ${responseData.title}</p>`;
                }
            };

            // 发送请求
            xhr.send();
        });
    </script>
</body>

</html>

示例代码(POST 请求)

收起

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>原生 AJAX POST 请求示例</title>
</head>

<body>
    <button id="postDataButton">发送数据</button>
    <div id="postResult"></div>

    <script>
        const postDataButton = document.getElementById('postDataButton');
        const postResultDiv = document.getElementById('postResult');

        postDataButton.addEventListener('click', () => {
            // 创建 XMLHttpRequest 对象
            const xhr = new XMLHttpRequest();

            // 打开连接
            xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);

            // 设置请求头
            xhr.setRequestHeader('Content-Type', 'application/json');

            // 准备请求体数据
            const data = {
                title: 'foo',
                body: 'bar',
                userId: 1
            };
            const jsonData = JSON.stringify(data);

            // 监听状态变化
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 201) {
                    // 请求成功,处理响应数据
                    const responseData = JSON.parse(xhr.responseText);
                    postResultDiv.innerHTML = `<p>新帖子 ID: ${responseData.id}</p>`;
                }
            };

            // 发送请求
            xhr.send(jsonData);
        });
    </script>
</body>

</html>

使用 jQuery 实现 AJAX

jQuery 是一个流行的 JavaScript 库,它对 XMLHttpRequest 进行了封装,提供了更简洁的 API 来实现 AJAX 请求。

示例代码(GET 请求)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery AJAX GET 请求示例</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

<body>
    <button id="jqueryFetchDataButton">使用 jQuery 获取数据</button>
    <div id="jqueryResult"></div>

    <script>
        $('#jqueryFetchDataButton').on('click', function () {
            $.ajax({
                url: 'https://jsonplaceholder.typicode.com/todos/1',
                method: 'GET',
                success: function (response) {
                    $('#jqueryResult').html(`<p>标题: ${response.title}</p>`);
                },
                error: function (error) {
                    console.error('请求出错:', error);
                }
            });
        });
    </script>
</body>

</html>

示例代码(POST 请求)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jQuery AJAX POST 请求示例</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

<body>
    <button id="jqueryPostDataButton">使用 jQuery 发送数据</button>
    <div id="jqueryPostResult"></div>

    <script>
        $('#jqueryPostDataButton').on('click', function () {
            const data = {
                title: 'foo',
                body: 'bar',
                userId: 1
            };

            $.ajax({
                url: 'https://jsonplaceholder.typicode.com/posts',
                method: 'POST',
                contentType: 'application/json',
                data: JSON.stringify(data),
                success: function (response) {
                    $('#jqueryPostResult').html(`<p>新帖子 ID: ${response.id}</p>`);
                },
                error: function (error) {
                    console.error('请求出错:', error);
                }
            });
        });
    </script>
</body>

</html>

2.Fetch的使用

fetch 是浏览器提供的一个用于发起网络请求的 API,它基于 Promise 实现,使用起来较为简洁和灵活。以下从基本使用、请求参数、处理响应、错误处理等方面详细介绍 fetch 的使用方法:

基本使用

fetch 最基本的用法是传入一个 URL 作为参数,它会发起一个 GET 请求并返回一个 Promise 对象,该 Promise 会在请求完成后被 resolve 为一个 Response 对象,代表服务器的响应。

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

代码解释

  1. fetch('https://api.example.com/data'):发起一个 GET 请求到指定的 URL。
  2. response.ok:检查响应的状态码是否在 200 - 299 范围内,如果不在则抛出错误。
  3. response.json():将响应的主体内容解析为 JSON 格式,它返回一个 Promise。
  4. data:最终得到的解析后的 JSON 数据。
  5. .catch():捕获请求过程中可能出现的错误并进行处理。

发送 POST 请求

如果需要发送 POST 请求,可以通过传入第二个参数(一个配置对象)来指定请求方法、请求头和请求体等信息。

const data = {
  name: 'John',
  age: 30
};

fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
})
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

代码解释

  1. method: 'POST':指定请求方法为 POST。
  2. headers:设置请求头,这里指定请求体的格式为 JSON。
  3. body: JSON.stringify(data):将 JavaScript 对象转换为 JSON 字符串作为请求体发送。

处理不同类型的响应

除了 JSON 格式的响应,fetch 还可以处理其他类型的响应,如文本、Blob 等。

处理文本响应

fetch('https://api.example.com/text')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.text();
  })
  .then(text => {
    console.log(text);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

处理 Blob 响应(用于下载文件等场景)

fetch('https://example.com/image.jpg')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.blob();
  })
  .then(blob => {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'image.jpg';
    link.click();
    URL.revokeObjectURL(url);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

设置请求超时

fetch 本身没有内置的超时机制,但可以结合 Promise.race 来实现超时功能。

function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchPromise = fetch(url, { ...options, signal });

  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => {
      controller.abort();
      reject(new Error('Request timed out'));
    }, timeout);
  });

  return Promise.race([fetchPromise, timeoutPromise]);
}

fetchWithTimeout('https://api.example.com/data', { method: 'GET' }, 3000)
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

代码解释

  1. AbortController 和 signal:用于取消请求。
  2. Promise.race:同时执行 fetchPromise 和 timeoutPromise,哪个先完成就返回哪个的结果。如果超时 timeoutPromise 先完成,则取消请求并抛出错误。

coding time

以下为你提供几个不同场景下使用 fetch 的完整代码示例,帮助你进一步掌握它的使用。

1. 简单的 GET 请求获取 JSON 数据

这个示例会从 JSONPlaceholder 这个免费的测试 API 获取一条待办事项数据并打印出来。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fetch GET Example</title>
</head>

<body>
    <script>
        // 发起 GET 请求
        fetch('https://jsonplaceholder.typicode.com/todos/1')
           .then(response => {
                // 检查响应状态
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                // 将响应数据解析为 JSON
                return response.json();
            })
           .then(data => {
                console.log('获取到的数据:', data);
            })
           .catch(error => {
                console.error('请求出错:', error);
            });
    </script>
</body>

</html>

2. 发送 POST 请求

下面的代码会向 JSONPlaceholder 的 API 发送一个 POST 请求,模拟创建一条新的待办事项。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fetch POST Example</title>
</head>

<body>
    <script>
        // 要发送的数据
        const todoData = {
            title: '完成前端项目',
            completed: false
        };

        // 发起 POST 请求
        fetch('https://jsonplaceholder.typicode.com/todos', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(todoData)
        })
           .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.json();
            })
           .then(data => {
                console.log('创建成功,返回的数据:', data);
            })
           .catch(error => {
                console.error('请求出错:', error);
            });
    </script>
</body>

</html>

3. 处理文本响应

此示例会从一个简单的文本文件 URL 获取文本内容并显示在页面上。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fetch Text Response Example</title>
</head>

<body>
    <div id="text-container"></div>
    <script>
        const textContainer = document.getElementById('text-container');

        // 发起 GET 请求获取文本
        fetch('https://example.com/sample.txt') 
           .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.text();
            })
           .then(text => {
                textContainer.textContent = text;
            })
           .catch(error => {
                console.error('请求出错:', error);
                textContainer.textContent = '无法获取文本内容';
            });
    </script>
</body>

</html>

4. 实现请求超时

下面代码实现了一个带有超时功能的 fetch 请求,若请求超过设定时间还未完成,会终止请求并报错。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fetch with Timeout Example</title>
</head>

<body>
    <script>
        function fetchWithTimeout(url, options = {}, timeout = 5000) {
            const controller = new AbortController();
            const signal = controller.signal;

            const fetchPromise = fetch(url, { ...options, signal });

            const timeoutPromise = new Promise((_, reject) => {
                setTimeout(() => {
                    controller.abort();
                    reject(new Error('请求超时'));
                }, timeout);
            });

            return Promise.race([fetchPromise, timeoutPromise]);
        }

        fetchWithTimeout('https://jsonplaceholder.typicode.com/todos/1', { method: 'GET' }, 3000)
           .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.json();
            })
           .then(data => {
                console.log('获取到的数据:', data);
            })
           .catch(error => {
                console.error('请求出错:', error);
            });
    </script>
</body>

</html>

3.Axios的使用

Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。它具有很多优点,比如支持拦截请求和响应、转换请求和响应数据、自动转换 JSON 数据等。以下是 Axios 的详细使用方法:

安装

  • 在浏览器中使用:可以通过 CDN 引入 Axios,在 HTML 文件的 <head> 标签中添加如下代码:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  • 在 Node.js 项目中使用:使用 npm 或 yarn 进行安装,在项目根目录下执行以下命令:
npm install axios
# 或者
yarn add axios

基本使用

发送 GET 请求

// 浏览器环境示例
axios.get('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => {
    console.log('响应数据:', response.data);
  })
  .catch(error => {
    console.error('请求出错:', error);
  });

// Node.js 环境示例
const axios = require('axios');

axios.get('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => {
    console.log('响应数据:', response.data);
  })
  .catch(error => {
    console.error('请求出错:', error);
  });

发送 POST 请求

const data = {
  title: 'foo',
  body: 'bar',
  userId: 1
};

axios.post('https://jsonplaceholder.typicode.com/posts', data)
  .then(response => {
    console.log('响应数据:', response.data);
  })
  .catch(error => {
    console.error('请求出错:', error);
  });

使用 async/await 语法

async function fetchData() {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
    console.log('响应数据:', response.data);
  } catch (error) {
    console.error('请求出错:', error);
  }
}

fetchData();

配置请求

在发送请求时,可以传递一个配置对象来设置请求的各种参数,如请求头、超时时间等。

axios({
  method: 'get',
  url: 'https://jsonplaceholder.typicode.com/todos/1',
  headers: {
    'Content-Type': 'application/json'
  },
  timeout: 5000 // 设置超时时间为 5 秒
})
  .then(response => {
    console.log('响应数据:', response.data);
  })
  .catch(error => {
    if (error.code === 'ECONNABORTED') {
      console.error('请求超时');
    } else {
      console.error('请求出错:', error);
    }
  });

拦截器

Axios 允许在请求或响应被处理之前对其进行拦截,可用于添加请求头、日志记录等操作。

// 添加请求拦截器
axios.interceptors.request.use(config => {
  // 在发送请求之前做些什么
  console.log('请求拦截器:', config);
  // 可以在这里添加请求头
  config.headers.Authorization = 'Bearer your_token';
  return config;
}, error => {
  // 对请求错误做些什么
  console.error('请求拦截器错误:', error);
  return Promise.reject(error);
});

// 添加响应拦截器
axios.interceptors.response.use(response => {
  // 对响应数据做点什么
  console.log('响应拦截器:', response);
  return response;
}, error => {
  // 对响应错误做点什么
  console.error('响应拦截器错误:', error);
  return Promise.reject(error);
});

取消请求

在某些情况下,可能需要取消正在进行的请求。可以使用 CancelToken 来实现。

const CancelToken = axios.CancelToken;
let cancel;

axios.get('https://jsonplaceholder.typicode.com/todos/1', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
})
  .then(response => {
    console.log('响应数据:', response.data);
  })
  .catch(error => {
    if (axios.isCancel(error)) {
      console.log('请求已取消:', error.message);
    } else {
      console.error('请求出错:', error);
    }
  });

// 取消请求
cancel('请求被用户取消');

并发请求

Axios 提供了 axios.all 方法来处理并发请求,使用 axios.spread 来处理多个响应。

function getUserAccount() {
  return axios.get('https://jsonplaceholder.typicode.com/users/1');
}

function getUserPermissions() {
  return axios.get('https://jsonplaceholder.typicode.com/permissions/1');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread((accountResponse, permissionsResponse) => {
    console.log('用户账户信息:', accountResponse.data);
    console.log('用户权限信息:', permissionsResponse.data);
  }))
  .catch(error => {
    console.error('请求出错:', error);
  });

以上就是 Axios 的常见使用方法,你可以根据具体需求灵活运用这些功能。