Skip to content

一文应对axios封装

1.axios封装

1.axios封装主要封装哪些功能?

在进行 Axios 封装时,主要目标是简化和优化 HTTP 请求的使用,并提供一致性的接口和处理。以下是一些常见的功能和优势,可以在 Axios 封装中实现:

  1. 统一的请求配置:通过封装,您可以设置默认的请求配置,如基本 URL、请求头、超时时间等,以避免在每个请求中重复设置。

  2. 拦截器:通过请求和响应拦截器,您可以在请求发送和响应接收之前/之后执行特定的逻辑。例如,您可以在请求拦截器中添加认证信息,或在响应拦截器中处理错误状态

    并且Axios 默认返回 Promise,您可以在封装中进一步处理 Promise 的响应,以便于更好的错误处理和链式调用。

  3. 简化API:封装可以将 Axios 的复杂性进行抽象,提供简化的方法来执行各种请求(GET、POST、PUT 等),并隐藏底层实现细节。——>重点

  4. 全局loading:可以在请求开始和结束时显示和隐藏全局的加载指示器,为用户提供更好的反馈。——>重点

  5. 二次API的模块化封装:封装可以将不同的 API 请求分散到不同的模块中(全都引入封装好的axios对象),然后再在具体的页面中进行使用,使代码更有组织性和可维护性。

  6. 取消请求:Axios 封装可以为每个请求创建一个取消令牌,允许您在需要时取消请求。这在用户导航离开页面(比如用户买个商品立马退出页面,可能这时候商品还没买完呢,所以应该取消请求)或取消多余的请求时特别有用。——> 重点

总的来说,Axios 封装的主要目标是提供更高级别的抽象,简化和优化 HTTP 请求的使用,并为您的应用程序提供一致的请求处理方式。不同项目的封装可能会有所不同,取决于项目需求和开发团队的偏好。

2.axios在简化API方面的封装

当封装 Axios 来简化 API 调用时,您可以创建一个包含常用请求方法(如 GET、POST、PUT 等)的 API 客户端。以下是一个简化的示例:

js
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com', // 设置基本 URL
  timeout: 10000, // 设置请求超时时间
});

// 封装常用请求方法:使用起来更加方便一点
export const get = (url, params) => apiClient.get(url, { params });
export const post = (url, data) => apiClient.post(url, data);
export const put = (url, data) => apiClient.put(url, data);
export const del = (url) => apiClient.delete(url);

// 其他自定义封装,如拦截器、错误处理等

export default apiClient;

注意:axios.create 是 Axios 提供的一个方法,用于创建一个自定义配置的 Axios 实例。通过创建实例,您可以在不同的请求中共享相同的配置,从而减少重复代码,并且可以根据不同的使用场景进行个性化配置。

简化API和不简化的对比:以get请求为例 ——> 可以大大简化,不用每次都写httpRequest了

不简化:

js
import httpRequest from "@/request";
export const getFootsByUserId = (userId: any) => {
  return httpRequest({
    method: "get",
    url: `/apit/foot/user/${userId}`,
    params: {
      page: 1,
      limit: 10
    },
  });
};

简化:

js
import { get } from '@/request';

export const getFootsByUserId = (userId: any) => {
  return get(`/api/foot/user/${userId}`, { page: 1, limit: 10 });
};

注意:查询参数和路径参数是可以一起使用的!

查询参数和路径参数(使用 / 参数)可以一起使用。路径参数通常用于标识资源的唯一标识,而查询参数用于传递其他信息,如过滤、排序、分页等。

以下是一个使用路径参数和查询参数的示例:

js
import { get } from '@/your-path-to-the-get-method';

const userId = 123; // 假设是用户的ID

get(`/api/foot/user/${userId}`, { page: 1, limit: 10 })
  .then(response => {
    // 处理响应数据
  })
  .catch(error => {
    // 处理错误
  });

在这个示例中,/api/foot/user/${userId} 是包含了路径参数的 URL,${userId} 会被实际的用户ID替代。同时,{ page: 1, limit: 10 } 是查询参数,用于传递分页信息。

生成的链接将会是:

bash
/api/foot/user/123?page=1&limit=10

这样的链接同时包含了路径参数和查询参数,服务器可以根据路径参数确定特定用户的足迹数据,并根据查询参数进行分页和限制。

3.axios的两种发送请求的配置方式

当使用 Axios 进行 HTTP 请求时,有两种常见的方式:基于配置对象的方式和直接使用 Axios 方法的方式。以下是对这两种方式的总结:

  1. 基于配置对象的方式
js
import axios from 'axios';
axios({
  method: "post",         // 请求方法
  url: "/apit/foot/been", // 请求的 URL
  data: data,             // 请求的数据
})
  .then(response => {
    // 处理响应数据
  })
  .catch(error => {
    // 处理错误
  });
  • 这种方式通过传递一个配置对象给封装的请求函数来发送请求。
  • 配置对象中的 method 字段指定请求方法,url 字段指定请求的 URL,data 字段用于传递请求数据。
  1. 直接使用 Axios 方法的方式
js
import axios from 'axios';

axios.get('/api/data')
  .then(response => {
    // 处理响应数据
  })
  .catch(error => {
    // 处理错误
  });
  • 这种方式直接使用 Axios 提供的方法(如 axios.get()axios.post())来发送请求。
  • 您可以通过不同的方法调用来发送不同类型的请求,如 GET、POST 等。

4.axios取消请求怎么做?

在使用 axios 发起请求时,有两种方法可以取消请求:

  1. 通过 CancelToken 实例来取消请求 您可以通过创建一个 CancelToken 实例并将其传递给请求的 config 对象中来实现取消请求。然后,在需要取消请求的地方,您可以调用 cancel 方法以发送取消请求信号。

以下是一个例子:

js
js复制代码import axios from 'axios';

const source = axios.CancelToken.source();

const fetchData = async () => {
  try {
    const res = await axios.get('/some/url', { cancelToken: source.token });
    console.log(res.data);
  } catch (error) {
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    } else {
      console.log(error);
    }
  }
};


// 在某个时刻取消请求
source.cancel('Operation canceled by the user.');

在上面的代码中,我们首先创建了一个名为 source 的 CancelToken 实例,并将其传递给请求的 config 对象中。然后,在需要取消请求的位置,我们通过调用 source.cancel() 方法来发送取消请求信号。如果请求已经被取消,则会抛出一个包含取消原因的错误,并且您可以在 catch 块中检查这个错误并处理它。

  1. 通过 cancel 属性来取消请求 另一种方法是直接在请求对象上设置 cancel 属性,该属性是一个函数。当您需要取消请求时,只需调用此函数即可。

以下是一个例子:

js
js复制代码import axios from 'axios';

const fetchData = async () => {
  const request = axios.get('/some/url');

  // 在某个时刻取消请求
  request.cancel('Operation canceled by the user.');

  try {
    const res = await request;
    console.log(res.data);
  } catch (error) {
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    } else {
      console.log(error);
    }
  }
};

在上面的代码中,我们首先发起一个请求,并将其分配给一个变量 request。然后,在需要取消请求的位置,我们通过调用 request.cancel() 方法来发送取消请求信号。如果请求已经被取消,则会抛出一个包含取消原因的错误,并且您可以在 catch 块中检查这个错误并处理它。

注意:我们就算取消请求,这个请求也可能已经到后端了!

5.全局Loading怎么保证只出现一个Loading呢?

要确保全局只出现一个加载指示器,您可以在显示加载指示器之前检查当前是否已经存在一个加载指示器实例。如果已经存在加载指示器,就不需要再创建新的实例。

以下是一个示例代码,演示如何通过维护一个加载指示器计数来保证全局只出现一个加载指示器:

js
import Vue from 'vue';
import axios from 'axios';
import { Loading } from 'element-ui';

// 创建一个封装的 Axios 实例
const customAxios = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
});

// 全局加载指示器计数
let loadingCount = 0;
let loadingInstance;

// 请求拦截器:在请求发送前显示加载指示器
customAxios.interceptors.request.use(config => {
  // 增加计数
  loadingCount++;

  // 如果计数为 1,显示加载指示器 ——> 这是关键
  if (loadingCount === 1) {
    loadingInstance = Loading.service();
  }

  return config;
}, error => {
  return Promise.reject(error);
});

// 响应拦截器:在响应返回后隐藏加载指示器
customAxios.interceptors.response.use(response => {
  // 减少计数
  loadingCount--;

  // 如果计数为 0,关闭加载指示器(只有为0的时候才关闭)
  if (loadingCount === 0 && loadingInstance) {
    loadingInstance.close();
  }

  return response;
}, error => {
  // 减少计数
  loadingCount--;

  // 如果计数为 0,关闭加载指示器
  if (loadingCount === 0 && loadingInstance) {
    loadingInstance.close();
  }

  return Promise.reject(error);
});

export default customAxios;

在这个示例中,我们维护了一个加载指示器计数 loadingCount,以及一个加载指示器实例 loadingInstance。在请求拦截器中,当需要显示加载指示器时,我们检查计数是否为 1(即没有其他请求正在显示加载指示器),然后创建加载指示器实例。在响应拦截器中,当需要隐藏加载指示器时,我们检查计数是否为 0,然后关闭加载指示器实例。

通过这种方式,您可以确保全局只会出现一个加载指示器,无论同时有多少个请求。这种实现方式可以在用户进行多个请求时提供更好的加载反馈,同时避免了重复的加载指示器。

2.fetch封装拦截器

1.fetch的介绍

基本介绍

fetch 是现代 Web 开发中用于发送网络请求的 API,它提供了一种更简洁、强大的方式来进行异步数据交互。相比于传统的 XMLHttpRequestfetch 更加灵活,支持 Promise,更易于使用和处理。

以下是关于 fetch 的一些基本介绍以及常用方法:

  1. 发送请求: 使用 fetch(url, options) 函数来发送请求。它接受两个参数:
    • url:请求的 URL。
    • options:一个可选的配置对象,用于指定请求的方法、头部、身份验证、请求体等。
  2. 配置对象 optionsoptions 配置对象可以包含以下属性:——> XHR只能配置一个url,这里能配置的要多的多(XHR请求头有单独设置方法,而具体数据是放在send方法里面的)
    • method:请求的方法,如 "GET"、"POST" 等。
    • headers:请求头部的对象,用于设置请求的头部信息。
    • body:请求的数据体,用于 POST 请求传递数据。
    • mode:请求的模式,如 "cors"、"no-cors" 等。
    • credentials:请求的凭证模式,如 "same-origin"、"include" 等。
  3. 处理响应: 使用 Promise 来处理响应。fetch 返回一个 Promise,你可以使用 .then() 方法来处理成功的响应,或使用 .catch() 方法来处理请求错误。
  4. 处理响应数据: 使用 .json() 方法可以将响应数据解析为 JSON 格式。也可以使用 .text() 方法来获取响应的文本形式。类似地,还有 .blob().arrayBuffer() 等方法。
  5. 错误处理: 在使用 Promise 的 .catch() 方法中处理请求错误。可以通过检查响应的状态码来判断请求的成功与否。
  6. 请求拦截和响应拦截fetch 并没有像 Axios 那样内置的拦截器,但你可以通过链式调用 Promise 来实现请求和响应的拦截处理。
  7. 异步/等待语法fetch 结合异步/等待语法(async/await)可以使代码更加清晰易读。

示例:

js
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    console.log('Data:', data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

总之,fetch 是一种现代、强大的网络请求 API,为前端开发者提供了更简洁、易用的方式来进行异步数据交互,同时支持 Promise 和现代的语法特性。

注意 fetch 返回的 promise 对象第一次的.then是联系服务器是否成功的信息,再往下第二次.then才是返回的数据!

——>关注分离的思想:把一件事分成几个步骤,每个步骤获取一次状态

fetch实例:没有进行优化

.then有两个参数,一个是成功(resolve)的回调,一个是失败(rejact)的回调!

js
search = async () => {
  //发送网络请求---使用fetch发送(未优化)
  fetch(`/api1/search/users2?q=${keyWord}`).then(
    //联系服务器
    response => { //成功的回调
      console.log('联系服务器成功了'); //注意:联系成功不代表请求成功,返回的内容可能是错误信息
      return response.json(); //response.json()是获取的数据,这个方法的返回值是一个 promise对象
    },
    error => { //失败的回调 ——> 不建议在这里写 error 回调,一般是在最后用.catch进行捕获即可!
      console.log('联系服务器失败了',error);
      return new Promise(()=>{}); //可以返回一个待定状态的 promise,会中断 promise 链,这样就不会走.then了!不能不写 return,否则会走成功的回调,因为 undefined 也是非 promise 对象,会返回成功状态的 promise!
    }
  ).then(
    //获取数据
    response => {console.log('获取数据成功了',response);},
    error => {console.log('获取数据失败了',error);}
  )
}

实例:catch统一处理错误

js
//发送网络请求---.catch统一处理错误
  fetch(`/api1/search/users2?q=${keyWord}`).then(
    //联系服务器
    response => { //成功的回调
      console.log('联系服务器成功了'); //注意:联系成功不代表请求成功,返回的内容可能是错误信息
      return response.json(); //response.json()是获取的数据——>会被包裹为 success 类型的 promise对象
    }
  ).then(
    //获取数据
    response => {console.log('获取数据成功了',response);},
  ).catch(error => {
      console.log('请求出错');
  })

实例:async+await优化

js
  //发送网络请求---使用fetch发送(使用 async+await 优化)
  try {
    //第一次获取 promise 对象
    const response = await fetch(`/api1/search/users2?q=${keyWord}`); //连接是否成功
    //第二次获取 promise 对象
    const data = await response.json(); //获取数据
    console.log(data);
    PubSub.publish("atguigu", { isLoading: false, users: data.items });
  } catch (error) { //处理错误内容
    console.log("请求出错", error);
    PubSub.publish("atguigu", { isLoading: false, err: error.message });
  }

2.fetch实现添加拦截器的功能

  • 我们要做的拦截是什么意思呢? 就是我们可以拦截页面上所有的fetch请求,我们可以在请求之前拦截请求头的信息,和在请求后拦截得到服务器响应的结果
  • 拦截器用法参考axios的拦截器用法

原理:

通过重写fetch方法,添加我们想要的功能,然后替换window下的fetch

js
window.fetch = Cfetch

1.对于fetch的封装(让它变成axios的类似样子,因为axios本身就基于XHR进行了封装):增加了拦截器对象,并使用拦截函数 ——> 关键的就是这里

思路:

1.构造一个拦截器对象,里面有相应的请求和响应的use方法,类似axios,当use被调用时就会把处理函数记录下来。

2.然后封装本来的fetch,这个函数返回一个Promise,在返回之前用请求的处理函数把参数值进行处理,在return 的Promise里面调用fetch,在返回结果之后使用响应处理函数把返回值进行处理,利用promise状态进行相应的返回!——> 关键点在这里

写一个封装函数的一种常见思路:

  • 返回值是一个Promised对象(把本身返回Promise的进行一个二次的包装,便于处理返回值Promise对象的状态)

  • 在return返回值之前,也就是函数内部还可以做一些操作

这样就实现了对于一个返回值为Promise对象的函数的包装,在其之前做了一些操作,在之后也进行了一些操作 ——> 这是考点!

具体实现:

js
/**
 * Cfetch:基于原生fetch封装了拦截器功能,暴露出来的Cfetch跟原生fetch用法一致,只是增加了拦截器功能。拦截器用法参考axios的拦截器用法。
 * 拦截器: interceptors
 */

// 定义用来存储拦截请求和拦截响应结果的处理和错误结果处理的函数集合(实际上如果只有一个处理函数的话,这里也没必要用数组了,用变量接收也可以!)
let interceptorsReq = []
let interceptorsReqError = []
let interceptorsRes = []
let interceptorsResError = []

// 复制一份原生fetch的方法,后面我们还是得调用原生的fetch,只是我们在fetch之上做一层封装,添加我们想要的功能
const OriginFetch = window.fetch

function Cfetch(input, init = {}) { //input是url,init是相关参数
  // interceptorsReq是拦截请求的拦截处理函数集合
  interceptorsReq.forEach(item => {
    init = item(init) // ——> 把参数做了一些封装
  })

  // 在原生fetch外面封装一个promise,为了在promise里面可以对fetch请求的结果做拦截处理。
  // 同时,保证Cfetch函数返回的结果是个promise对象。
  return new Promise((resolve, reject) => {
    // 发起fetch请求,fetch请求的形参是接收上层函数的形参
    OriginFetch(input, init).then(res => {
      // interceptorsRes是拦截响应结果的拦截处理函数集合
      interceptorsRes.forEach(item => {
        // 拦截器对响应结果做处理,把处理后的结果返回给响应结果。
        res = item(res)
      })
      // 将拦截器处理后的响应结果resolve出去
      resolve(res)
    })
      .catch(err => {
        // interceptorsResError是拦截响应错误结果的拦截处理函数集合
        interceptorsResError.forEach(item => {
        // 拦截器对响应错误结果做处理,把处理后的结果返回给响应结果。
          err = item(err)
        })
        reject(err)
      })
  })
}

// interceptors拦截器提供request和response两种拦截器功能。
// 可以通过request和response的use方法来绑定两种拦截器的处理函数:
// use方法接收两个参数,参数为一个callback函数,callback函数用来作为拦截器的成功处理函数,一个errorCallback作为错误处理函数
// request.use方法会把callback放在interceptorsReq中,等待执行。
// response.use方法会把callback放在interceptorsRes中,等待执行。
// 拦截器的处理函数callback接收一个参数:
// request拦截器的callback接收的是请求发起前的config;
// response拦截器的callback接收的是网络请求的response结果。
const interceptors = {
  request: {
    use(callback, errorCallback) {
      interceptorsReq.push(callback)
      errorCallback && interceptorsReqError.push(errorCallback)
    }
  },
  response: {
    use(callback, errorCallback) {
      interceptorsRes.push(callback)
      errorCallback && interceptorsResError.push(errorCallback)
    }
  }
}

// 暴露导出这个对象
export default {
  Cfetch,
  interceptors
}

这里解释下为什么我们的interceptors对象,不写在Cfetch函数上面呢,如Cfetch.interceptors = {}, 因为这样写呢,有些网页他们做了反注入的话,会检查原生的fetch上面是不是多了其他属性。

所以我就不写在Cfetch函数上面了,这样他们就检查不出来。道高一丈,魔高一尺,哈哈哈,如果对方又做了其他反注入,大家可以发挥想法换一种思路注入。

2.全局fetch(这里其实就和axios类似了)

js
import { Cfetch, interceptors } from './fetch'
// 这里是我项目使用到的js-cookie库,主要是为了拿到token,你们这里改成你们获取token的方式即可
import Cookies from 'js-cookie'

/**
 * config 自定义配置项
 * @param withoutCheck 不使用默认的接口状态校验,直接返回 response
 * @param returnOrigin 是否返回整个 response 对象,为 false 只返回 response.data
 * @param showError 全局错误时,是否使用统一的报错方式
 * @param canEmpty 传输参数是否可以为空
 * @param mock 是否使用 mock 服务
 * @param timeout 接口请求超时时间,默认10秒
 */
let configDefault = {
  showError: true,
  canEmpty: false,
  returnOrigin: false,
  withoutCheck: false,
  mock: false,
  timeout: 10000
}

// 添加请求拦截器
interceptors.request.use(config => {
  // 这里是我项目使用到的js-cookie库,主要是为了拿到token,你们这里改成你们获取token的方式即可
  const token = Cookies.get('access_token')
  configDefault = Object.assign({
    responseType: 'json',
    headers: {
      'Content-Type': 'application/json;charset=utf-8',
      authorization: `Bearer ${token}`
    },
  }, configDefault, config)
  console.log('添加请求拦截器 configDefalut ==>', configDefault)
  return configDefault
})

// 添加响应拦截器 ——> 给use方法传递了请求时的一个拦截的方法
interceptors.response.use(async response => {
  console.log('拦截器response ==>', response)
  console.log('configDefault', configDefault)

  // TODO: 这里是复制一份结果处理,在这里可以做一些操作
  const res = await resultReduction(response.clone())

 // HTTP 状态码 2xx 状态入口,data.code 为 200 表示数据正确,无任何错误
  if (response.status >= 200 && response.status < 300) {
    // return res //返回.json()处理之后的数据
    return response
  } else { // 非 2xx 状态入口
    if (configDefault.withoutCheck) { // 不进行状态状态检测
      return Promise.reject(res)
    }
    return Promise.reject(res)
  }
})

// 结果处理,fetch请求响应结果是promise,还得处理 ——> 根据不同的responseType类型,返回不同的数据格式
async function resultReduction(response) {
  let res = ''
  switch (configDefault.responseType) {
    case 'json':
      res = await response.json()
      break
    case 'text':
      res = await response.text()
      break
    case 'blod':
      res = await response.blod()
      break
    default:
      res = await response.json()
      break
  }
  console.log('结果处理', res)
  return res
}

function request(method, path, data, config) { //这里封装的fetch请求
  let myInit = {
    method,
    ...configDefault,
    ...config,
    body: JSON.stringify(data),
  }
  if (method === 'GET') { //get方法处理查询参数
    let params = ''
    if (data) {
    // 对象转url参数
      params = JSON.stringify(data).replace(/:/g, '=')
        .replace(/"/g, '')
        .replace(/,/g, '&')
        .match(/\{([^)]*)\}/)[1]
    }
    return Cfetch(`${path}?${params}`, {
        ...configDefault,
        ...config,
    })
  }

  return Cfetch(path, myInit)
}

// get请求方法使用封装
function get(path, data, config) {
  return request('GET', path, data, config)
}

// post请求方法使用封装
function post(path, data, config) {
  return request('POST', path, data, config)
}

// put请求方法使用封装
function put(path, data, config) {
  return request('PUT', path, data, config)
}

// delete请求方法使用封装
function del(path, data, config) {
  return request('DELETE', path, data, config)
}

export default {
  fetch: Cfetch,
  get,
  post,
  put,
  delete: del
}

3.在页面中使用

js
import fetchApi from './fetchApi'

winodw.fetch = fetchApi.fetch // 这里其实本质上没有实质性作用,我们并没有使用这个window.fetch

fetchApi.get('http://www.xxx', {id: '1'})

fetchApi.post('http://www.xxx', {id: '1'})

成功结果打印如图:

img

注意:这里对于js模块化的导出导入代码可能理解不够到位,不清楚interceptors.response.use在什么时候执行的?

其实interceptors.response.use这个代码会在import fetchApi from './fetchApi'这句话的时候被执行(当然前提是这个vue页面被加载了)

也就是说导出的js模块会在别的文件引入并使用这个模块时执行,而将要被导出的代码本身是不会执行的,只有被使用的时候才会执行!

在使用了import fetchApi from './fetchApi'这句话的时候,就相当于把所有的上面1和2的代码都引入进来了,interceptors.response.use这种方法自然会自动地执行。

并且注意,不同的文件引入这个fetchApi都会重新创建各种的函数和变量,因为不同文件之间的代码肯定是互不干预的。

3.XHR封装拦截器

1.基本介绍

XMLHttpRequest 是一个用于在浏览器中发送 HTTP 请求的 JavaScript 对象。它提供了一种在客户端与服务器之间进行数据交换的方式,允许你在不刷新整个页面的情况下获取或发送数据。XMLHttpRequest 是现代 Web 开发中重要的组成部分,常用于实现异步数据交互和构建单页应用。

以下是关于 XMLHttpRequest 的一些基本介绍以及常用方法:

  1. 创建 XMLHttpRequest 对象: 你可以通过 new XMLHttpRequest() 来创建一个 XMLHttpRequest 对象,然后使用该对象来进行请求。
  2. open 方法open(method, url, async) 方法用于初始化一个请求。它接受三个参数:
    • method:请求的方法,如 "GET"、"POST" 等。
    • url:请求的 URL。
    • async:表示请求是否异步,通常设置为 true
  3. 设置请求头: 你可以使用 setRequestHeader(header, value) 方法来设置请求头,如设置 Content-TypeAuthorization 等。
  4. send 方法send(data) 方法用于发送请求。它接受一个参数 data,表示要发送的数据。对于 GET 请求,可以不传递参数。对于 POST 请求,你可以将要发送的数据作为参数传递给 send 方法。
  5. 监听状态变化: 使用 onreadystatechange 事件来监听 XMLHttpRequest 对象的状态变化。状态通过 readyState 属性来表示,常见的状态包括 0(未初始化)、1(已打开)、2(已发送)、3(正在接收数据)、4(已完成)。
  6. 获取响应数据: 你可以使用 responseText 来获取响应数据的文本形式,或使用 responseXML 获取 XML 形式的响应数据。在状态为 4 时,你可以获取到完整的响应数据。
  7. 处理响应: 通常在 onreadystatechange 事件的回调中检查状态是否为 4,以及响应的状态码是否为成功(如 status === 200),然后处理响应数据。
  8. 错误处理: 当请求失败时,你可以检查 statusstatusText 属性来判断失败的原因。

请注意,XMLHttpRequest 是旧的异步请求方法,在现代 Web 开发中,往往使用 fetch 或第三方库(如 Axios)来处理异步请求,因为它们提供了更方便的 API 和更好的处理能力。

基本使用

js
function getDataWithXMLHttpRequest() {
  const xhr = new XMLHttpRequest();
  const url = 'https://api.example.com/data'; // 替换成实际的 API 地址

  xhr.onreadystatechange = function() { //设置请求完成之后的操作
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        const responseData = JSON.parse(xhr.responseText);
        // 处理响应数据
        console.log(responseData);
      } else {
        // 处理错误
        console.error('Request failed');
      }
    }
  };

  xhr.open('GET', url, true); //配置一个请求
  xhr.send(); //正式发送请求
}

// 调用函数发起数据请求
getDataWithXMLHttpRequest();

2.具体封装

拦截包括两部分

  1. 请求(request)拦截
  2. 请求完(response)拦截
html
<script>
      class XMLHttp {
        request = function (param) {};
        response = function (param) {};
      }
      let http = new XMLHttp();

	// 初始化 拦截XMLHttpRequest
	function initXMLHttpRequest() {
          let open = XMLHttpRequest.prototype.open;
          //open本来就是XMLHttpRequest的原型上面的一个方法
          XMLHttpRequest.prototype.open = function(...args){
            let send = this.send;
            let _this = this //注意:这里的this是指向XMLHttpRequest这个对象的
            let post_data = []
            this.send = function (...data) {
              post_data = data;
              return send.apply(_this, data)
            }
            // 请求前拦截
            http.request(args)

            this.addEventListener('readystatechange', function () {
              if (this.readyState === 4) {
                let config = {
                  url: args[1],
                  status: this.status,
                  method: args[0],
                  data: post_data
                }
                // 请求后拦截
                http.response({config, response: this.response})
              }
            }, false)
            return open.apply(this, args);
          }
      }

 	// 初始化页面
	(function () {
        // XMLHttpRequest 拦截
        http.request = function (param) {
        //   console.log(param, "---request");
        };
        http.response = function (res) {
            console.log(res, "---response");
        }
        // 初始化 XMLHttpRequest
        initXMLHttpRequest();

        // 模拟数据请求 (此处写自己要使用的请求)
        // request();

      })();
 </script>