阅读视图

发现新文章,点击刷新页面。

从零掌握 Ajax:一次请求带你读懂异步数据加载原理

一、精炼理解:Ajax 到底是啥?

Ajax = 在不刷新页面的情况下,和服务器交换数据并动态更新页面
它让 Web 页面拥有“应用级”的体验——局部更新、响应式交互、更少等待。核心实现长期基于浏览器内建对象 XMLHttpRequest(XHR) ,现代开发则多用 fetch / Axios,但底层思想相同:异步请求 + 数据驱动视图


二、核心流程

  1. 创建 XHR 实例

    const xhr = new XMLHttpRequest();
    
  2. 打开连接

    xhr.open('GET', '/api/members', true); // true => 异步
    
  3. 可选:设置请求头 / 超时 / 响应类型

    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.responseType = 'json'; // 直接得到对象(现代浏览器)
    xhr.timeout = 5000; // 毫秒
    
  4. 发送请求

    xhr.send();
    
  5. 监听状态 / 处理响应

    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          const data = xhr.responseType === 'json' ? xhr.response : JSON.parse(xhr.responseText);
          // 更新 DOM
        } else {
          // 处理非 200 的情况
        }
      }
    };
    
  6. 清理/取消

    xhr.abort(); // 主动取消请求
    

三、readyState 一眼看懂

  • 0 UNSENT:未调用 open()
  • 1 OPENED:调用了 open(),可 setRequestHeadersend
  • 2 HEADERS_RECEIVED:已接收响应头(可读取 status)
  • 3 LOADING:接收响应体中(可处理流式数据)
  • 4 DONE:响应完成,数据就绪

调试建议:打印 readyStatestatus,快速定位在哪个阶段失败。


四、常见坑与解决方法

1. 同步请求会卡死 UI

xhr.open(..., false) 是同步,会阻塞主线程。不要在浏览器中使用同步请求,除非你非常清楚场景(几乎没有)。

2. 跨域(CORS)错误

浏览器会阻止不满足 CORS 策略的请求。后端需设置 Access-Control-Allow-Origin,或通过代理绕过开发时限制。

3. JSON 解析报错

JSON.parse 报错说明服务器没返回合法 JSON。排查响应 Content-Type、响应体首尾空白和服务器异常栈。

4. 超时与重试

设置合理 xhr.timeout,并在 ontimeout 中进行用户提示或重试逻辑(避免无限重试)。

5. 状态码不是 200

处理 4xx/5xx、304 等不同类型响应,给用户明确提示而非只在控制台打印错误。

6. 网络抖动与幂等

POST 请求若可重试需保证幂等(或在服务端做去重),避免重复提交造成的数据问题。


五、XHR 与 fetch/Axios 对比(实用速览)

特性 XHR fetch Axios
语法风格 回调/事件 Promise(更现代) Promise + 自动 JSON 处理
支持流 有(onprogress) 有(ReadableStream) 基于 fetch/XHR,封装更好
自动 JSON 解析 .json() 是(响应自动转换)
超时设置 有(timeout 需 AbortController 内置超时配置
更灵活的进度 是(上传/下载 progress) 比较复杂 封装上传/下载进度支持

建议:新项目优先用 fetch 或 Axios;但遇到上传进度/低层控制时,XHR 的事件模型仍然非常有用。


六、实用示例:可靠的异步请求

<ul id="members"></ul>
<script>
  const ul = document.getElementById('members');

  function fetchMembers() {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://api.github.com/orgs/lemoncode/members', true);
    xhr.responseType = 'json'; // 现代浏览器直接得到对象
    xhr.timeout = 7000;

    xhr.onreadystatechange = () => {
      if (xhr.readyState !== 4) return;
      if (xhr.status === 200) {
        const data = xhr.response;
        ul.innerHTML = data.map(u => `<li>${u.login}</li>`).join('');
      } else {
        ul.innerHTML = `<li>请求失败:${xhr.status} ${xhr.statusText}</li>`;
      }
    };

    xhr.ontimeout = () => {
      ul.innerHTML = '<li>请求超时,请重试</li>';
    };

    xhr.onerror = () => {
      ul.innerHTML = '<li>网络错误,请检查连接</li>';
    };

    xhr.send();
    return xhr; // 若需要外部取消请求,可保留此引用并调用 xhr.abort()
  }

  fetchMembers();
</script>

七、总结

掌握 Ajax,不只是知道如何发请求,而是理解请求生命周期、错误处理与用户体验折衷——把网络的不确定性,优雅地交给代码去处理。

❌