原生 Ajax 请求实现

使用 XMLHttpRequest 和 Fetch API 实现 Ajax 请求

问题

在实际开发中,如何使用原生 JavaScript 实现 Ajax 请求?

解答

XMLHttpRequest 方式

function ajax(options) {
  const { method = 'GET', url, data = null, headers = {} } = options;

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

    // 2. 初始化请求
    xhr.open(method, url, true);

    // 3. 设置请求头
    for (const key in headers) {
      xhr.setRequestHeader(key, headers[key]);
    }

    // 4. 监听状态变化
    xhr.onreadystatechange = function () {
      if (xhr.readyState !== 4) return;

      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`Request failed with status ${xhr.status}`));
      }
    };

    // 5. 监听错误
    xhr.onerror = function () {
      reject(new Error('Network error'));
    };

    // 6. 发送请求
    xhr.send(data ? JSON.stringify(data) : null);
  });
}

// 使用示例
ajax({
  method: 'GET',
  url: 'https://api.example.com/users'
}).then(data => {
  console.log(data);
}).catch(err => {
  console.error(err);
});

Fetch API 方式

async function request(url, options = {}) {
  const {
    method = 'GET',
    data = null,
    headers = {},
    timeout = 10000
  } = options;

  // 创建 AbortController 用于超时控制
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...headers
      },
      body: data ? JSON.stringify(data) : null,
      signal: controller.signal
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timeout');
    }
    throw error;
  }
}

// 使用示例
request('https://api.example.com/users', {
  method: 'POST',
  data: { name: 'John' }
}).then(data => {
  console.log(data);
});

封装通用请求类

class Http {
  constructor(baseURL = '') {
    this.baseURL = baseURL;
  }

  request(url, options) {
    return fetch(this.baseURL + url, {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...options.headers
      },
      body: options.body ? JSON.stringify(options.body) : null
    }).then(res => {
      if (!res.ok) throw new Error(res.statusText);
      return res.json();
    });
  }

  get(url, params = {}) {
    const query = new URLSearchParams(params).toString();
    const fullUrl = query ? `${url}?${query}` : url;
    return this.request(fullUrl, { method: 'GET' });
  }

  post(url, data) {
    return this.request(url, { method: 'POST', body: data });
  }

  put(url, data) {
    return this.request(url, { method: 'PUT', body: data });
  }

  delete(url) {
    return this.request(url, { method: 'DELETE' });
  }
}

// 使用示例
const http = new Http('https://api.example.com');
http.get('/users', { page: 1 });
http.post('/users', { name: 'John' });

关键点

  • XMLHttpRequest readyState:0-未初始化,1-已打开,2-已发送,3-接收中,4-完成
  • Fetch 不会 reject HTTP 错误:需要手动检查 response.okresponse.status
  • Fetch 超时处理:使用 AbortController 实现,XMLHttpRequest 可直接设置 xhr.timeout
  • 跨域请求:需要服务端配置 CORS,或使用代理
  • Fetch 优势:语法简洁、基于 Promise、支持 Stream