项目亮点之Axios进行二次封装

最近辅导的学员里有很多中后台管理系统项目,不知道怎么挖掘这类项目的亮点,或者做这些项目的时候不知道如何提升自己的项目能力。中后台管理系统中最常见的一个亮点或者难点就是:二次封装Axios,对于这个点大家都略知一二,但是在面试过程中以及平时工作总结时,往往觉的过于简单,无从谈起。接下来跟大家深入探讨一些关于 二次封装Axios 的一些问题

基本操作

  1. 创建config.js文件

在项目的根目录下创建一个名为config.js的文件,用于配置请求路径。可以在这个文件中定义一些常用的请求参数和配置。

export const BASE_URL = '/api'// 常用接口地址
export const loginIp = '/loginIp'// 登陆功能地址
  1. 创建axios.js文件

在项目的根目录下创建一个名为axios.js的文件,引入axios库和vue、router等其他模块。在这个文件中,可以配置基本的请求路径和参数。例如:

import axios from 'axios';
import Vue from 'vue';
import router from 'router';
import store from 'store';

const service = axios.create({
  baseURL: process.env.VUE_APP_MOCK === 'true' ? process.env.VUE_APP_BASE_URL : '/api'// 根据环境变量判断是否使用Mock数据
  timeout5000 // 请求超时时间
});
  1. 配置请求拦截器

在axios.js文件中可以配置请求拦截器,对每个请求进行预处理和后处理。例如:

service.interceptors.request.use(function (config{
  // 在发送请求之前做些什么
  // 可以在这里添加请求头、参数等
  return config;
}, function (error{
  // 对请求错误做些什么
  return Promise.reject(error);
});
  1. 配置响应拦截器

在axios.js文件中还可以配置响应拦截器,对每个响应进行预处理和后处理。例如:

service.interceptors.response.use(function (response{
  // 对响应数据做点什么,如格式化、转换等
  return response;
}, function (error{
  // 对响应错误做点什么,如统一处理、抛出异常等
  return Promise.reject(error);
});
  1. 封装API接口

在axios.js文件中可以封装一些常用的API接口,方便开发人员使用。例如:

// 封装get请求接口
const get = (url, params) => {
  return service({ url, method'get', params });
};
export default get;

数据接口处理的一些痛点以及解决方案

1. 接口统一管理

在后台管理系统中,当项目规模逐渐增大并涉及多个接口时,如果没有合理的接口管理方案,可能会导致以下问题:

  1. 接口散乱:各个模块开发人员可能各自为战,定义和维护接口,导致接口命名、参数传递等不一致,给项目的开发和维护带来困扰。
  2. 接口冲突:不同模块间可能会存在相同或相似的接口,容易造成重复劳动和混淆。
  3. 可维护性差:缺乏统一的接口管理机制,使得维护和修改接口变得困难,影响开发效率。

通过二次封装Axios,可以实现统一管理所有的API请求。通过创建一个独立的接口管理模块或单独的接口管理文件,该模块或文件集中管理所有接口的定义和文档,并提供相关工具和规范。

假设有一个管理后台项目,其中包含用户管理、订单管理和商品管理三个模块。每个模块都需要与后端进行数据交互,这就涉及到多个接口。

首先,创建一个api.js文件,用于统一管理所有接口。在该文件中,定义了每个模块所需的接口,并提供了具体的请求方法。

// api.js

// 用户管理接口
export const userAPI = {
  getUserList'/api/user/list',
  addUser'/api/user/add',
  updateUser'/api/user/update'
};

// 订单管理接口
export const orderAPI = {
  getOrderList'/api/order/list',
  addOrder'/api/order/add',
  updateOrder'/api/order/update'
};

// 商品管理接口
export const productAPI = {
  getProductList'/api/product/list',
  addProduct'/api/product/add',
  updateProduct'/api/product/update'
};

然后,在各个模块的代码中,只需要引入api.js文件,并使用其中定义的接口即可。

// UserManagement.vue

import { userAPI } from './api';

// 使用用户管理接口
axios.get(userAPI.getUserList)
  .then(response => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  });

通过创建一个独立的接口管理模块或文件,可以帮助管理后台项目更好地统一管理接口,提高开发效率和可维护性。不同开发人员在不同模块中使用相同的接口命名,减少了接口冲突和散乱的问题。同时,接口的维护和修改也变得更加方便,只需要在api.js文件中修改即可,无需在多个模块中逐个搜索和修改对应的接口。

2. 多host的统一解决方案

在后台管理的项目中,通常会遇到需要同时连接到不同的后端服务器或API时,需要根据不同的环境或配置切换不同的host。这可能在以下几种情况下出现:

  1. 多域名: 项目需要在多个域名下运行,每个域名都有其自己的请求路由和数据处理逻辑。
  2. 子域划分: 只运行在一个域名下,可能需要为不同的子域设置不同的配置或者路由。
  3. 云环境: 在云环境中,项目可能部署在不同的服务器上,每个服务器可能有不同的IP地址或者域名。

为了解决这个问题,需要实现一种可以根据请求的来源确定如何处理请求机制。

使用Axios作为HTTP客户端的情况下,可以通过配置Axios的基础URL来解决这个问题。基础URL是Axios在发送请求时默认的URL前缀。

假设两个主机,一个是http://host1.com,另一个是http://host2.com,可以这样配置Axios:

const axios = require('axios').default;

const instance = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL // 根据环境变量确定基础URL
});

export default instance;

在上面的代码中,process.env.VUE_APP_BASE_URL是一个环境变量,它的值取决于你的应用程序运行在哪里。例如,如果你的应用程序在host1.com上运行,那么process.env.VUE_APP_BASE_URL的值就是http://host1.com。这样,Axios就会默认在这个URL下发送请求。

然后,可以在代码中使用这个Axios实例发送请求,而不需要每次都指定基础URL:

import axios from './axios';

axios.get('/api/data')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });

在上面的代码中,Axios会向http://[VUE_APP_BASE_URL]/api/data发送GET请求,根据不同的环境自动切换host地址,从而实现多host支持。开发人员只需要在配置文件中添加或修改对应环境的host地址,无需在项目的每个请求中手动改变host。

通过使用配置文件和动态获取host地址的方式,可以解决管理后台项目中多host问题。这样可以轻松地切换不同的后端服务器或API,并提供更好的灵活性和可维护性。

3. 区分不同的env

在后台管理的项目中,通常涉及到在不同的环境(例如开发环境、测试环境、生产环境等)中运行不同的代码和配置。这有助于确保代码在不同的环境中具有正确的行为,并保护生产环境的敏感信息不被泄露。

在使用Axios作为HTTP客户端的情况下,可以通过配置Axios的基础URL来解决区分env的问题。每个环境都可以有一个不同的基础URL,这样Axios发送请求时就会使用相应环境的URL。

下面用一个实际的项目例子,演示如何使用Axios支持区分env:

  1. 首先,在项目的根目录下创建一个文件,例如axios-config.js,用于配置Axios实例。
  2. axios-config.js中,根据环境变量的值来设置Axios的基础URL。例如,在开发环境中,可以将基础URL设置为http://api.dev.example.com,在生产环境中,可以将基础URL设置为http://api.example.com
const axios = require('axios').default;

const instance = axios.create({
  baseURL: process.env.VUE_APP_BASE_URL // 根据环境变量确定基础URL
});

module.exports = instance;
  1. 在项目中使用Axios发送请求时,导入并使用配置好的Axios实例。例如,在一个Vue组件中使用Axios发送GET请求:
import axiosInstance from './axios-config';

export default {
  async data() {
    try {
      const response = await axiosInstance.get('/api/data');
      console.log(response.data);
    } catch (error) {
      console.error(error);
    }
  }
};

在上面的代码中,Axios会根据环境变量的值确定基础URL,并向相应的URL发送GET请求。例如,如果在开发环境中运行该组件,Axios将向http://api.dev.example.com/api/data发送GET请求;如果在生产环境中运行该组件,Axios将向http://api.example.com/api/data发送GET请求。

  1. 根据项目的需要,可以在不同的环境变量中设置不同的基础URL。例如,可以在.env文件中根据环境变量的值设置基础URL:
    • 在开发环境中,可以将VUE_APP_BASE_URL=http://api.dev.example.com添加到.env文件中。
    • 在生产环境中,可以将VUE_APP_BASE_URL=http://api.example.com添加到.env文件中。这样,在不同的环境中运行项目时,Axios就会使用相应环境的URL发送请求。
  2. 确保在项目中使用正确的环境变量名。在上面的示例中,我们使用了VUE_APP_BASE_URL作为环境变量名。可以根据项目的实际情况选择适合的环境变量名。

4. 更好的支持restful风格

支持 RESTful 风格意味着应用程序的各个组件之间通过 REST(Representational State Transfer)协议进行通信。REST 是一种基于 HTTP 协议构建 Web 服务的软件架构风格,它使用不同的 HTTP 方法(如 GET、POST、PUT、DELETE 等)来访问和操作资源。

RESTful 风格的优点包括:

  1. 简单性:RESTful 架构风格简单明了,易于理解和实现。
  2. 灵活性:RESTful 服务可以独立于语言和平台,这意味着客户端可以使用不同的编程语言和工具与服务器进行通信。
  3. 可扩展性:RESTful 服务可以通过不同的方式进行扩展,例如添加新的资源或使用不同的媒体类型。

使用 Axios 支持 RESTful 风格的示例:

  1. 首先,安装 Axios:
npm install axios
  1. 创建一个新的文件,例如 api.js,用于封装 RESTful 服务的请求逻辑。在这个文件中,我们可以定义不同 HTTP 方法对应的请求路径和参数。例如:
import axios from 'axios';

const apiUrl = 'https://api.example.com'// RESTful 服务的基础 URL

// 获取资源
export const getResource = (id) => {
  return axios.get(`${apiUrl}/resources/${id}`);
};

// 创建资源
export const createResource = (data) => {
  return axios.post(`${apiUrl}/resources`, data);
};

// 更新资源
export const updateResource = (id, data) => {
  return axios.put(`${apiUrl}/resources/${id}`, data);
};

// 删除资源
export const deleteResource = (id) => {
  return axios.delete(`${apiUrl}/resources/${id}`);
};
  1. 在需要使用 RESTful 服务的地方导入并使用这些请求逻辑。例如,在一个 Vue 组件中使用 Axios 发送 GET 请求:
import api from './api'// 导入封装好的 RESTful 请求逻辑

export default {
  async data() {
    try {
      const response = await api.getResource(1); // 发送 GET 请求获取资源
      console.log(response.data);
    } catch (error) {
      console.error(error);
    }
  }
};

5. 支持取消请求

在管理后台的项目中,支持取消请求是一种常见的需求。当用户在提交一个请求后,可能会需要取消该请求以避免不必要的网络通信或防止不必要的操作。

使用 Axios 作为 HTTP 客户端,可以通过配置取消令牌(Cancellation Token)来实现取消请求的功能。取消令牌允许您在请求开始之前取消该请求。

使用 Axios 实现取消请求的功能:

  1. 在项目中安装 Axios:
npm install axios
  1. 创建一个新的 JavaScript 文件,例如 axios-cancel.js,用于封装取消请求的逻辑。
  2. axios-cancel.js 中,导入 Axios 并创建一个 Axios 实例:
import axios from 'axios';

const instance = axios.create();

export default instance;
  1. 在需要使用取消请求的地方导入并使用该实例。以下是一个使用取消请求的示例:
import axiosInstance from './axios-cancel';

// 创建一个取消令牌对象
const CancelToken = axios.CancelToken;
const source = CancelToken.source(); // 生成取消令牌源对象

// 发送请求时传入取消令牌源对象
axiosInstance.get('/api/data', {
  cancelToken: source.token, // 将取消令牌绑定到请求上
  timeout5000 // 设置请求超时时间(以毫秒为单位)
}).then(response => {
  console.log(response.data); // 处理响应数据
}).catch(error => {
  if (axios.isCancel(error)) {
    console.log('请求被取消:', error.message); // 打印取消消息
  } else {
    console.error(error); // 处理其他错误情况
  }
});

// 在需要取消请求的地方调用取消令牌的 throwSource 方法,将取消令牌源对象传递给取消令牌对象并触发取消事件。例如,当用户点击取消按钮时:
source.throw(); // 触发取消事件,请求将被取消

在上面的示例中,首先创建了一个取消令牌源对象 source,然后将该对象绑定到请求上,通过 cancelToken 选项将源对象传递给请求配置。在需要取消请求的地方,调用 throw() 方法来触发取消事件,这将导致请求被取消。如果请求被取消,可以通过捕获错误来处理取消事件。注意,在使用取消令牌时,需要确保在请求开始之前设置取消令牌,并在请求完成之前调用 throw() 方法来触发取消事件。

6. 支持接口错误重试

由于网络不稳定或服务端错误等原因,接口请求可能会失败。为了提高系统的健壮性和稳定性,可以实现接口错误重试机制,即当请求失败时,自动重新发送请求一定次数,以增加请求成功的几率。

假设我们有一个管理后台项目,需要向后端发送请求获取用户列表。如果请求失败,我们希望自动进行重试,最多重试3次。

首先,在项目的请求模块中,可以使用axios提供的interceptors拦截器来处理请求错误,并进行重试逻辑的实现。

// api.js

import axios from 'axios';

const instance = axios.create({
  baseURL'/api',
  timeout5000
});

// 添加响应拦截器
instance.interceptors.response.use(
  response => {
    // 处理正常响应
    return response;
  },
  error => {
    if (axios.isRetryableError(error) && error.config && error.config.retryCount < 3) {
      // 如果是可重试的错误且重试次数未达到上限,则进行重试
      error.config.retryCount = (error.config.retryCount || 0) + 1;
      return new Promise((resolve) => {
        setTimeout(() => resolve(axios(error.config)), 1000); // 延迟1秒后重新发送请求
      });
    }
    // 处理错误响应
    return Promise.reject(error);
  }
);

// 导出实例
export default instance;

在上述代码中,使用了axios的interceptors.response拦截器来处理响应。如果响应发生错误且满足重试条件(可重试的错误、重试次数未达到上限),则会进行重试操作。

接下来,在其他文件中,我们可以直接引入并使用上述创建的axios实例,例如:

// UserManagement.vue

import api from './api';

const fetchUserList = () => {
  return api.get('/users');
};

fetchUserList()
  .then(response => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  });

通过上述方式,在用户列表请求函数中,如果遇到请求失败的情况,axios会自动进行重试最多3次。每次重试之间有1秒的延迟。这样,即使在请求过程中遇到网络不稳定或服务端错误等问题,项目也具备了一定的容错能力,并且可以提高请求成功的概率。

7. 支持缓存

当用户多次访问相同的数据时,如果每次都需要从数据库或其他远程服务器获取数据,会导致不必要的网络延迟和服务器负载。通过缓存,可以将经常访问的数据保存在本地,减少网络请求的次数,提高系统的响应速度和用户体验。

假设有一个管理后台项目,需要获取用户列表,并希望将用户列表数据进行缓存,减少对后端的频繁请求。

首先,在项目的请求模块中,我们可以使用axios提供的interceptors拦截器来处理缓存逻辑。

// api.js

import axios from 'axios';

const instance = axios.create({
  baseURL'/api',
  timeout5000
});

// 缓存对象
const cache = {};

// 添加请求拦截器
instance.interceptors.request.use(
  config => {
    if (config.cache) {
      const url = config.url;
      // 如果缓存中存在相同的URL,则直接返回缓存数据
      if (cache[url]) {
        return Promise.resolve(cache[url]);
      }
    }
    return config;
  },
  error => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  response => {
    const config = response.config;
    if (config.cache) {
      const url = config.url;
      // 将数据存入缓存
      cache[url] = response;
    }
    return response;
  },
  error => {
    // 处理错误响应
    return Promise.reject(error);
  }
);

// 导出实例
export default instance;

在上述代码中,使用了axios的interceptors.requestinterceptors.response拦截器来处理请求和响应,并实现了对缓存数据的处理。

接下来,在其他文件中,我们可以直接引入并使用上述创建的axios实例,例如:

// UserManagement.vue

import api from './api';

const fetchUserList = () => {
  return api.get('/users', { cachetrue });
};

fetchUserList()
  .then(response => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  });

通过在请求函数中传递{ cache: true }的配置参数,我们告诉axios我们希望对这个接口进行缓存。如果缓存中已经存在相同的URL,则会直接返回缓存中的数据,而不会发送新的请求。

这样,在后续的调用中,如果需要获取用户列表数据,将会直接从缓存中获取,避免了对后端的额外请求。

在实际项目中,可以根据具体的需求和场景选择合适的缓存策略。例如,可以根据URL、时间戳等方式进行缓存的识别和更新。同时,需要注意缓存的过期时间和内存占用问题,避免过长时间的缓存导致数据的不更新,以及过大的缓存占用导致系统的性能下降。

8. 支持限流

当大量用户同时访问某个接口或进行大量请求时,系统可能会面临巨大的负载压力,导致响应速度变慢、服务不可用等问题。通过限流,可以控制每个用户的请求频率和并发请求数量,避免系统过载,提高系统的响应速度和用户体验。

假设我们有一个管理后台项目,需要发送请求获取用户列表,而我们希望限制每秒钟只能发送5个请求。

首先,在项目的请求模块中,我们可以使用axios提供的interceptors拦截器来处理请求限流逻辑。

// api.js

import axios from 'axios';

const instance = axios.create({
  baseURL'/api',
  timeout5000
});

// 限流计数器
let requestCount = 0;

// 添加请求拦截器
instance.interceptors.request.use(
  config => {
    if (requestCount >= 5) {
      // 如果请求数量已达到上限,则取消此次请求
      return Promise.reject(new Error('请求过于频繁,请稍后再试。'));
    }
    requestCount++;
    return config;
  },
  error => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  response => {
    requestCount--;
    return response;
  },
  error => {
    requestCount--;
    // 处理错误响应
    return Promise.reject(error);
  }
);

// 导出实例
export default instance;

在上述代码中,我们使用了axios的interceptors.requestinterceptors.response拦截器来处理请求和响应,并实现了限流逻辑。

接下来,在其他文件中,我们可以直接引入并使用上述创建的axios实例,例如:

// UserManagement.vue

import api from './api';

const fetchUserList = () => {
  return api.get('/users');
};

fetchUserList()
  .then(response => {
    // 处理数据
  })
  .catch(error => {
    // 处理错误
  });

通过上述方式,在每次发送请求之前,我们会对请求计数器进行检查。如果请求数量已达到上限(这里是5个),则会直接返回一个错误提示,而不会发送请求。在响应拦截器中,无论请求成功还是失败,都会将请求计数器减1。

通过使用axios的拦截器和限流逻辑,可以实现管理后台项目中的限流功能。这种机制可以有效地控制请求频率,保护系统免受恶意攻击或过多的请求干扰,提高系统的稳定性和可用性。

最后