当前位置:首页 > 问答 > 正文

异步请求 前端开发 ajax.js及其在前端异步请求封装中的应用

聊聊前端开发中的ajax.js封装技巧

2025年8月最新动态
根据最新的前端生态调研报告,超过82%的现代Web应用仍依赖异步请求处理核心业务逻辑,尽管Fetch API和WebSocket使用率持续上升,但经过良好封装的Ajax工具库在复杂业务场景下的表现依然抢眼——特别是在需要兼容旧版浏览器或精细控制请求生命周期的项目中。

为什么我们还在讨论Ajax?

别看现在前端框架满天飞,但说到最基础的异步请求,很多团队还是会选择自己封装一套ajax.js,原因很简单:

异步请求 前端开发 ajax.js及其在前端异步请求封装中的应用

  1. 可控性:自己写的代码,想加什么功能就加什么
  2. 一致性:全项目用同一套请求逻辑,维护起来不头疼
  3. 兼容性:不用等到框架更新才能用上新特性

举个真实例子:某电商网站在大促时发现,直接使用axios在某些低端安卓机上会出现内存泄漏,而他们自己封装的轻量级ajax.js却稳如老狗。

一个够用的ajax.js长什么样?

先看个基础版骨架:

class Ajax {
  constructor() {
    this.xhr = new XMLHttpRequest();
    this.timeout = 5000;
  }
  request(method, url, data, headers) {
    return new Promise((resolve, reject) => {
      this.xhr.open(method, url, true);
      // 设置超时
      this.xhr.timeout = this.timeout;
      // 设置请求头
      Object.keys(headers).forEach(key => {
        this.xhr.setRequestHeader(key, headers[key]);
      });
      this.xhr.onload = () => {
        if (this.xhr.status >= 200 && this.xhr.status < 300) {
          resolve(JSON.parse(this.xhr.responseText));
        } else {
          reject(new Error(`请求失败: ${this.xhr.status}`));
        }
      };
      this.xhr.onerror = () => reject(new Error('网络错误'));
      this.xhr.ontimeout = () => reject(new Error('请求超时'));
      method === 'GET' ? this.xhr.send() : this.xhr.send(data);
    });
  }
  get(url, params) {
    const query = new URLSearchParams(params).toString();
    return this.request('GET', `${url}?${query}`);
  }
  post(url, data) {
    return this.request('POST', url, JSON.stringify(data), {
      'Content-Type': 'application/json'
    });
  }
}

实战中我们常加哪些功能?

请求拦截器

class Ajax {
  constructor() {
    // ...原有代码
    this.beforeRequest = [];
    this.afterResponse = [];
  }
  useBeforeRequest(fn) {
    this.beforeRequest.push(fn);
  }
  useAfterResponse(fn) {
    this.afterResponse.push(fn);
  }
  async request(method, url, data, headers) {
    // 执行前置钩子
    for (const fn of this.beforeRequest) {
      const modified = await fn({ method, url, data, headers });
      ({ method, url, data, headers } = modified || {});
    }
    // ...原有请求逻辑
    // 执行后置钩子
    let finalResponse = response;
    for (const fn of this.afterResponse) {
      finalResponse = await fn(finalResponse);
    }
    return finalResponse;
  }
}

自动重试机制

requestWithRetry(method, url, data, headers, retry = 3) {
  return new Promise((resolve, reject) => {
    const attempt = (remaining) => {
      this.request(method, url, data, headers)
        .then(resolve)
        .catch(err => {
          if (remaining > 0 && this.shouldRetry(err)) {
            setTimeout(() => attempt(remaining - 1), 1000);
          } else {
            reject(err);
          }
        });
    };
    attempt(retry);
  });
}
// 根据错误类型决定是否重试
shouldRetry(error) {
  return [
    'ECONNABORTED',  // 超时
    'ETIMEDOUT',     // 连接超时
    'ENETDOWN'       // 网络问题
  ].some(code => error.message.includes(code));
}

性能优化小技巧

  1. 请求去重:对于相同参数的GET请求,可以设置短期缓存
  2. 并发控制:限制同时进行的请求数量
  3. 取消请求:实现类似axios的CancelToken
// 取消请求实现示例
class Ajax {
  constructor() {
    this.pendingRequests = new Map();
  }
  request(method, url) {
    const requestId = `${method}-${url}`;
    const controller = new AbortController();
    this.pendingRequests.set(requestId, controller);
    fetch(url, {
      method,
      signal: controller.signal
    }).finally(() => {
      this.pendingRequests.delete(requestId);
    });
  }
  cancel(requestId) {
    if (this.pendingRequests.has(requestId)) {
      this.pendingRequests.get(requestId).abort();
    }
  }
}

现代项目中的定位

在2025年的技术栈中,自己封装ajax.js更适合:

异步请求 前端开发 ajax.js及其在前端异步请求封装中的应用

  • 需要支持IE11等老旧浏览器的项目
  • 对请求性能有极致要求的场景
  • 需要深度定制请求流程的特殊业务

如果是新启动的现代项目,更推荐基于fetch或axios进行二次封装,毕竟社区生态更完善,但无论如何,理解底层原理永远有价值——当你的请求库突然报出诡异错误时,这些知识能帮你快速定位问题。

最后的小建议:封装自己的ajax.js时,记得写好单元测试,请求相关的bug往往在特定网络环境下才会暴露,好的测试用例能帮你省下大量调试时间。

发表评论