Ajax

68 阅读9分钟

一、先搞懂2个根本问题:什么是请求?为什么需要请求?

1. 什么是“请求”?

  • 生活类比:你(浏览器/前端)向超市(服务器)要牛奶(数据/资源),“要”的动作就是请求,超市给你牛奶的动作是响应
  • 专业定义:客户端(浏览器)向服务器发起的“获取/提交数据/资源”的主动行为,通过HTTP/HTTPS协议传输。

2. 为什么需要请求?

  • 静态网页:无请求,内容写死在HTML里,无法更新(比如本地打开的纯文本页面);
  • 动态网页:通过请求获取服务器最新数据(如新闻、评论)或提交数据(如注册、发布内容),让网页“活”起来。

二、核心概念:什么是Ajax?(异步请求的核心方案)

1. Ajax定义

Ajax(Asynchronous JavaScript and XML)是一套异步数据交互方案,核心组合:JS逻辑 + XHR/Fetch(请求工具) + JSON/XML(数据格式)

  • 核心关键词:异步(请求不阻塞页面)、无刷新(局部更新,不用整页刷)、数据交互(只传数据,不传完整HTML)。

2. Ajax的价值

解决早期“同步请求”的痛点:

  • 同步请求:点击按钮后页面卡住,需等待完整HTML返回才能操作;
  • Ajax异步请求:请求时页面正常操作,只获取需要的数据,效率更高。

3. Ajax与XHR的关系

  • XHR是“工具”:浏览器内置的底层API,专门实现异步请求,是Ajax的核心依赖;
  • Ajax是“方案”:用XHR(或Fetch)实现异步数据交互的技术组合;
  • 结论:用XHR写异步请求,就是在写Ajax代码。

三、XHR基础:Ajax的核心工具(异步请求信使)

XHR(XMLHttpRequest)是实现Ajax的核心工具,以下是必须掌握的基础:

1. XHR核心属性(控制请求/获取响应)

属性名作用描述
readyState请求状态码(0-4),标志请求生命周期(0=未初始化,4=请求完成)
statusHTTP响应状态码(200=成功,404=资源未找到,500=服务器错误)
responseText服务器返回的字符串格式数据(常用)
timeout请求超时时间(毫秒,如5000=5秒超时)

2. XHR核心事件(监听请求状态)

事件名触发时机用途
onloadreadyState === 4(请求完成)处理响应数据(常用)
onerror网络错误(断网、地址无效)处理网络异常
ontimeout超过timeout时间未响应处理超时异常

3. XHR使用固定步骤(Ajax请求模板)

  1. 创建XHR实例 → 2. 配置请求(method、url、异步) → 3. (可选)设置请求头/超时 → 4. 监听响应 → 5. 发送请求

四、XHR实战:简化版Ajax请求(无页面渲染)

所有实战代码仅用控制台打印结果,聚焦请求核心逻辑,无需关注页面渲染。

4.1 环境准备

  • 创建文件夹ajax-xhr-simple,内部结构:
ajax-xhr-simple/
├─ data.json  (本地数据源)
└─ xhr-demo.html  (请求代码)
  • data.json内容:
{ "status": "success", "data": ["前端教程", "Ajax实战"] }
  • 启动服务器:VS Code安装「Live Server」,右键xhr-demo.html→「Open with Live Server」(本地请求必须用服务器,否则跨域)。

4.2 实战1:Ajax+XHR异步请求本地JSON(GET)

<!DOCTYPE html>
<html lang="zh-CN">
<body>
  <script>
    // 1. 创建XHR实例(Ajax的请求工具)
    const xhr = new XMLHttpRequest();

    // 2. 配置Ajax请求:GET方式、请求地址、async=true(异步,Ajax核心)
    xhr.open('GET', 'data.json', true);

    // 3. 设置超时时间(5秒)
    xhr.timeout = 5000;

    // 4. 监听响应(请求完成后触发,处理数据)
    xhr.onload = function() {
      // 判断请求是否成功:HTTP状态码200-299(服务器成功响应)
      if (xhr.status >= 200 && xhr.status < 300) {
        // 解析JSON字符串为JS对象(Ajax常用数据格式)
        const response = JSON.parse(xhr.responseText);
        console.log('Ajax GET请求本地JSON成功:', response);
      } else {
        console.error('Ajax GET请求失败,状态码:', xhr.status);
      }
    };

    // 5. 监听网络错误
    xhr.onerror = function() {
      console.error('Ajax GET请求网络错误');
    };

    // 6. 监听超时
    xhr.ontimeout = function() {
      console.error('Ajax GET请求超时(5秒)');
    };

    // 7. 发送请求(GET请求无请求体,传null)
    xhr.send(null);
  </script>
</body>
</html>
  • 运行结果:控制台打印{status: "success", data: ["前端教程", "Ajax实战"]}

4.3 实战2:Ajax+XHR请求真实API(GET)

使用免费测试API(JSONPlaceholder,支持跨域):

<!DOCTYPE html>
<html lang="zh-CN">
<body>
  <script>
    // 1. 创建XHR实例
    const xhr = new XMLHttpRequest();

    // 2. 配置Ajax请求:真实API地址(获取文章列表,带参数limit=2)
    xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts?limit=2', true);

    // 3. 监听响应
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        const posts = JSON.parse(xhr.responseText);
        console.log('Ajax GET请求真实API成功:', posts);
      } else {
        console.error('API请求失败,状态码:', xhr.status);
      }
    };

    // 4. 发送请求
    xhr.send(null);
  </script>
</body>
</html>
  • 运行结果:控制台打印2条来自远程服务器的文章数据。

4.4 实战3:Ajax+XHR提交数据(POST)

POST用于提交数据,核心是设置请求头和请求体:

<!DOCTYPE html>
<html lang="zh-CN">
<body>
  <script>
    // 1. 创建XHR实例
    const xhr = new XMLHttpRequest();

    // 2. 配置Ajax POST请求:方法为POST,地址为提交接口
    xhr.open('POST', 'https://jsonplaceholder.typicode.com/comments', true);

    // 3. 关键:设置请求头,告诉服务器请求体是JSON格式(POST必选)
    xhr.setRequestHeader('Content-Type', 'application/json');

    // 4. 监听响应
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        const response = JSON.parse(xhr.responseText);
        console.log('Ajax POST提交成功,服务器返回:', response);
      } else {
        console.error('POST提交失败,状态码:', xhr.status);
      }
    };

    // 5. 准备提交的数据(JSON对象)
    const postData = {
      name: 'Ajax测试',
      email: 'test@example.com',
      body: '这是POST提交的测试数据'
    };

    // 6. 发送请求:将JSON对象转为字符串(POST请求必须传请求体)
    xhr.send(JSON.stringify(postData));
  </script>
</body>
</html>
  • 运行结果:控制台打印服务器返回的提交结果(含自动生成的ID)。

五、关键知识:什么是跨域?为什么会跨域?怎么解决?

在本地请求或调用不同域名API时,经常会遇到“跨域错误”——这是前端开发的高频问题,必须掌握。

1. 什么是跨域?

  • 定义:浏览器的“同源策略”限制,当请求的协议、域名、端口三者任意一个与当前页面不同时,就属于跨域。
  • 同源示例:当前页面http://localhost:5500,请求http://localhost:5500/data.json(协议、域名、端口都相同,同源,允许请求);
  • 跨域示例:
    1. 本地双击HTML(协议file://)请求data.json(跨域);
    2. 页面http://localhost:5500请求https://api.xxx.com(协议不同,跨域);
    3. 页面http://a.com请求http://b.com(域名不同,跨域);
    4. 页面http://localhost:5500请求http://localhost:8080(端口不同,跨域)。

2. 为什么会有跨域限制?(同源策略的目的)

为了安全!防止恶意网站窃取你的数据:

  • 比如你登录了银行网站(https://bank.com),又打开了恶意网站(https://hack.com);
  • 若没有同源策略,恶意网站可通过请求获取你银行的登录状态和数据,造成安全风险。

3. 常见跨域解决方案(0基础友好)

(1)本地开发:用服务器环境(解决本地跨域)

  • 问题:直接双击HTML文件(file://协议)请求本地JSON,跨域报错;
  • 解决:用VS Code「Live Server」或Python临时服务器打开文件,让页面和请求地址同源(如http://localhost:5500)。

(2)真实项目:CORS(跨域资源共享,最常用)

  • 原理:服务器在响应头中添加Access-Control-Allow-Origin: *(允许所有域名请求),告诉浏览器“这个请求是安全的,可以放行”;
  • 示例:我们用的JSONPlaceholder API,服务器就配置了CORS,所以http://localhost:5500能正常请求它;
  • 特点:前端无需改代码,由后端配置,简单高效。

(3)其他方案:JSONP(兼容旧浏览器)

  • 原理:利用<script>标签不受同源策略限制的特性,通过动态创建<script>标签请求数据;
  • 局限:仅支持GET请求,不支持POST,现在已逐渐被CORS替代。

(4)开发环境:代理服务器(前端本地代理)

  • 原理:在本地开发环境(如Vue、React脚手架)配置代理,让请求先转发到本地代理服务器,再由代理服务器请求目标API(代理服务器无同源限制);
  • 示例:Vue脚手架配置vue.config.jsdevServer.proxy,React配置package.jsonproxy

3. 跨域错误示例(控制台报错)

Access to XMLHttpRequest at 'https://api.xxx.com' from origin 'http://localhost:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • 意思:目标服务器未配置CORS,浏览器阻止了跨域请求。

六、Ajax进阶:封装通用XHR请求函数(简化重复代码)

多次写XHR代码会发现重复逻辑太多,封装成通用函数,后续使用只需一行代码:

6.1 封装代码(支持GET/POST,带跨域相关注释)

/**
 * 通用Ajax请求封装(基于XHR)
 * @param {string} method - 请求方式:GET/POST
 * @param {string} url - 请求地址(本地JSON或API,注意跨域问题)
 * @param {Object} [options={}] - 可选配置
 * @param {Object} [options.data] - POST请求体数据
 * @param {number} [options.timeout=5000] - 超时时间
 * @returns {Promise<Object>} - 成功返回解析后的数据,失败返回错误
 */
function ajax(method, url, options = {}) {
  const { data = null, timeout = 5000 } = options;

  return new Promise((resolve, reject) => {
    // 1. 创建XHR实例
    const xhr = new XMLHttpRequest();

    // 2. 配置请求(强制异步,Ajax核心)
    xhr.open(method.toUpperCase(), url, true);

    // 3. 设置超时
    xhr.timeout = timeout;

    // 4. POST请求设置请求头(告诉服务器数据格式)
    if (data && method.toUpperCase() === 'POST') {
      xhr.setRequestHeader('Content-Type', 'application/json');
    }

    // 5. 监听响应成功
    xhr.onload = function() {
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch (e) {
          resolve(xhr.responseText); // 非JSON格式返回原始数据
        }
      } else {
        reject(new Error(`请求失败:状态码${xhr.status}`));
      }
    };

    // 6. 错误处理(含跨域错误,跨域时会触发onerror)
    xhr.onerror = () => reject(new Error('请求失败:网络错误或跨域问题'));
    xhr.ontimeout = () => reject(new Error(`请求超时(${timeout}ms)`));

    // 7. 处理请求体数据
    const requestData = data ? JSON.stringify(data
                                             ) : null;

    // 8. 发送请求
    xhr.send(requestData);
  });
}

// 简化GET/POST方法(直接调用)
ajax.get = (url, options) => ajax('GET', url, options);
ajax.post = (url, data, options) => ajax('POST', url, { ...data, ...options });

6.2 封装后使用示例

<!DOCTYPE html>
<html lang="zh-CN">
<body>
  <script>
    // 粘贴上面的封装函数...

    // 1. 用封装函数请求本地JSON(无跨域)
    async function fetchLocal() {
      try {
        const data = await ajax.get('data.json');
        console.log('封装函数请求本地JSON成功:', data);
      } catch (error) {
        console.error('请求失败:', error.message);
      }
    }
    fetchLocal();

    // 2. 用封装函数请求真实API(支持CORS,无跨域)
    async function fetchApi() {
      try {
        const posts = await ajax.get('https://jsonplaceholder.typicode.com/posts?limit=2');
        console.log('封装函数请求API成功:', posts);
      } catch (error) {
        console.error('API请求失败:', error.message);
      }
    }
    fetchApi();
  </script>
</body>
</html>

七、总结:核心知识点回顾

  1. 请求:前后端交互的桥梁,无请求则无动态网页;
  2. Ajax:异步请求方案,核心是“异步、无刷新、数据交互”;
  3. XHR:实现Ajax的底层工具,固定使用步骤(创建→配置→监听→发送);
  4. 跨域:同源策略导致,解决方案有本地服务器、CORS、代理等;
  5. 封装:减少重复代码,提高开发效率,支持async/await简化回调。