一、简介

axios 是一个轻量的HTTP客户端,它基于 XMLHttpRequest 服务来执行 HTTP 请求,支持丰富的配置,支持 Promise,支持浏览器端和 Node.js 端。自Vue2.0起,尤大大宣布取消对vue-resource 的官方推荐,转而推荐 axios。现在 axios 已经成为大部分 Vue 开发者的首选。( 如果你还不熟悉 axios,可以在这里查看它的API。)

封装前,先来看下,不封装的情况下,一个实际项目中axios请求的样子。

大概是长这样:

axios('http://localhost:3000/data', {
  method: 'GET',
  timeout: 1000,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'xxx',
  },
  transformRequest: [function (data, headers) {
    return data;
  }],
  // 其他请求配置...
})
.then((data) => {
  // todo: 真正业务逻辑代码
  console.log(data);
}, (err) => {
  if (err.response.status === 401) {
  // handle authorization error
  }
  if (err.response.status === 403) {
  // handle server forbidden error
  }
  // 其他错误处理.....
  console.log(err);
});

可以看到在这段代码中,页面代码逻辑只在第15行处,上方的一大块请求配置代码和下方一大块响应错误处理代码,几乎跟页面功能没有关系,而且每个请求中这些内容都差不多,甚至有的部分完全一样。

二、封装后

1.封装步骤

封装的本质就是在待封装的内容外面添加各种东西,然后把它们作为一个新的整体呈现给使用者,以达到扩展和易用的目的。

封装 axios 要做的事情,就是把所有HTTP请求共用的配置,事先都在axios上配置好,预留好必要的参数和接口,然后把它作为新的axios返回。

目录结构如下(由Vue-cli 3.0 生成):

|--public/
|--mock/
|   |--db.json  # 我新建的接口模拟数据
|--src/
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...

2.封装目标

在 Home 页,发起 axios 请求时就像调用一个只有少量参数的方法一样简单,这样我就可以专注业务代码了。

1. 将 axios 封装到一个独立的文件

  • 在src下创建 utils/http.js 文件
  cd src
  mkdir utils
  touch http.js
  • 引入 axios
  // src/utils/http.js
  import axios from 'axios';
  • 创建一个类
  //src/utils/http.js
  //...
  class NewAxios {
  
  }
  • 给不同环境配置不同请求地址

根据process.env.NODE_ENV 配置不同的 baseURL,使项目只需执行相应打包命令,就可以在不同环境中自动切换请求主机地址。

// src/utils/http.js

//...
const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000',
    test: 'http://localhost:3001',
  }[env];
  if (!base) {
    base = '/';
  }
  return base;
};

class NewAxios {
  constructor() {
    this.baseURL = getBaseUrl(process.env.NODE_ENV);
  }
}
  • 配置超时时间

timeout属性,我一般设置10秒。

// src/utils/http.js

//...
class NewAxios {
  constructor() {
    //...
    this.timeout = 10000;
  }
}
  • 配置允许携带凭证

widthCredentials属性设为true

// src/utils/http.js

//...
class NewAxios {
  constructor() {
    //...
    this.withCredentials = true;
  }
}
  • 给这个类创建实例上的方法request

request 方法里,创建新的axios实例,接收请求配置参数,处理参数,添加配置,返回axios实例的请求结果(一个promise对象)。

你也可以不创建,直接使用默认导出的axios实例,然后把所有配置都放到它上面,不过这样一来整个项目就会共用一个axios实例。虽然大部分项目下这样够用没问题,但是有的项目中不同服务地址的请求和响应结构可能完全不同,这个时候共用一个实例就没办法支持了。所以为了封装可以更通用,更具灵活性,我会使用axioscreate方法,使每次发请求都是新的axios实例。

// src/utils/http.js

//...
class NewAxios {
  //...
  request(options) {
    // 每次请求都会创建新的axios实例。
    const instance = axios.create();
    const config = { // 将用户传过来的参数与公共配置合并。
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout,
      withCredentials: this.withCredentials,
    };
    // 配置拦截器,支持根据不同url配置不同的拦截器。
    this.setInterceptors(instance, options.url);
    return instance(config); // 返回axios实例的执行结果
  }
}

因为拦截器配置内容比较多,所以封装成一个内部函数了。

  • 配置请求拦截器

在发送请求前对请求参数做的所有修改都在这里统一配置。比如统一添加token凭证、统一设置语言、统一设置内容类型、指定数据格式等等。做完后记得返回这个配置,否则整个请求不会进行。

我这里就配置一个token

// src/utils/http.js

//...
class NewAxios {
  //...
  // 这里的url可供你针对需要特殊处理的接口路径设置不同拦截器。
  setInterceptors = (instance, url) => { 
    instance.interceptors.request.use((config) => { // 请求拦截器
      // 配置token
      config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || '';
      return config;
    }, err => Promise.reject(err));
  }
  //...
}
  • 配置响应拦截器

在请求的thencatch处理前对响应数据进行一轮预先处理。比如过滤响应数据,更多的,是在这里对各种响应错误码进行统一错误处理,还有断网处理等等。

我这里就判断一下403和断网。

// src/utils/http.js

//...
class NewAxios {
  //...
  setInterceptors = (instance, url) => {
    //...
    instance.interceptors.response.use((response) => { // 响应拦截器
      // todo: 想根据业务需要,对响应结果预先处理的,都放在这里
      console.log();
      return response;
    }, (err) => {
      if (err.response) { // 响应错误码处理
        switch (err.response.status) {
          case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if (!window.navigator.online) { // 断网处理
        // todo: jump to offline page
        return -1;
      }
      return Promise.reject(err);
    });
  }
  //...
}

另外,在拦截器里,还适合放置loading等缓冲效果:在请求拦截器里显示loading,在响应拦截器里移除loading。这样所有请求就都有了一个统一的loading效果。

  • 默认导出新的实例
  // src/utils/http.js
  
  //...
  export default new NewAxios();

最后完整的代码如下:

// src/utils/http.js

import axios from 'axios';

const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000',
    test: 'http://localhost:3001',
  }[env];
  if (!base) {
    base = '/';
  }
  return base;
};

class NewAxios {
  constructor() {
    this.baseURL = getBaseUrl(process.env.NODE_ENV);
    this.timeout = 10000;
    this.withCredentials = true;
  }

  // 这里的url可供你针对需要特殊处理的接口路径设置不同拦截器。
  setInterceptors = (instance, url) => {
    instance.interceptors.request.use((config) => {
      // 在这里添加loading
      // 配置token
      config.headers.AuthorizationToken = localStorage.getItem('AuthorizationToken') || '';
      return config;
    }, err => Promise.reject(err));

    instance.interceptors.response.use((response) => {
      // 在这里移除loading
      // todo: 想根据业务需要,对响应结果预先处理的,都放在这里
      return response;
    }, (err) => {
      if (err.response) { // 响应错误码处理
        switch (err.response.status) {
          case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if (!window.navigator.online) { // 断网处理
        // todo: jump to offline page
        return -1;
      }
      return Promise.reject(err);
    });
  }

  request(options) {
    // 每次请求都会创建新的axios实例。
    const instance = axios.create();
    const config = { // 将用户传过来的参数与公共配置合并。
      ...options,
      baseURL: this.baseURL,
      timeout: this.timeout,
      withCredentials: this.withCredentials,
    };
    // 配置拦截器,支持根据不同url配置不同的拦截器。
    this.setInterceptors(instance, options.url);
    return instance(config); // 返回axios实例的执行结果
  }
}

export default new NewAxios();

现在 axios 封装算是完成了80%。我们还需要再进一步把axios和接口结合再封装一层,才能达到我在一开始定的封装目标。

3. 使用新的 axios 封装API

  • 在 src 目录下新建 api 文件夹。把所有涉及HTTP请求的接口统一集中到这个目录来管理。
  • 新建 home.js。我们需要把接口根据一定规则分好类,一类接口对应一个js文件。这个分类可以是按页面来划分,或者按模块等等。为了演示更直观,我这里就按页面来划分了。实际根据自己的需求来定。
  • 使用新的 axios 封装API(固定url的值,合并用户传过来的参数),然后命名导出这些函数。
// src/api/home.js 

import axios from '@/utils/http';
export const fetchData = options => axios.request({
  ...options,
  url: '/data',
});
export default {};

在 api 目录下新建 index.js,把其他文件的接口都在这个文件里汇总导出。

 // src/api/index.js
  
  export * from './home';

这层封装将我们的新的axios封装到了更简洁更语义化的接口方法中。

现在我们的目录结构长这样:

|--public/
|--mock/
|   |--db.json  # 接口模拟数据
|--src/
|   |--api/     # 所有的接口都集中在这个目录下
|       |--home.js  # Home页面里涉及到的接口封装在这里
|       |--index.js # 项目中所有接口调用的入口
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--utils/
|       |--http.js  # axios封装在这里
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...

4.使用封装后的axios

现在我们要发HTTP请求时,只需引入 api 下的 index.js 文件就可以调用任何接口了,并且用的是封装后的 axios。

// src/views/Home.vue

<template>
  <div class="home">
    <h1>This is home page</h1>
  </div>
</template>

<script>
// @ is an alias to /src
import { fetchData } from '@/api/index';

export default {
  name: 'home',
  mounted() {
    fetchData()  // axios请求在这里
      .then((data) => {
        console.log(data);
      })
      .catch((err) => {
        console.log(err);
      });
  },
};
</script>

axios请求被封装在fetchData函数里,页面请求压根不需要出现任何axios API,悄无声息地发起请求获取响应,就像在调用一个简单的 Promise 函数一样轻松。并且在页面中只需专注处理业务功能,不用被其他事物干扰。

到此这篇关于vue项目中axios的封装请求的文章就介绍到这了,更多相关vue中axios封装内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

vue项目中axios的封装请求的更多相关文章

  1. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. vue自定义加载指令v-loading占位图指令v-showimg

    这篇文章主要为大家介绍了vue自定义加载指令和v-loading占位图指令v-showimg的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. vue使用动画实现滚动表格效果

    这篇文章主要为大家详细介绍了vue使用动画实现滚动表格效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  4. 关于Vue 监控数组的问题

    这篇文章主要介绍了Vue 监控数组的示例,主要包括Vue 是如何追踪数据发生变化,Vue 如何更新数组以及为什么有些数组的数据变更不能被 Vue 监测到,对vue监控数组知识是面试比较常见的问题,感兴趣的朋友一起看看吧

  5. Vue子组件props从父组件接收数据并存入data

    这篇文章主要介绍了Vue子组件props从父组件接收数据并存入data的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. 解决axios:"timeout of 5000ms exceeded"超时的问题

    这篇文章主要介绍了解决axios:"timeout of 5000ms exceeded"超时的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  7. Vue h函数的使用详解

    本文主要介绍了Vue h函数的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  8. VUE响应式原理的实现详解

    这篇文章主要为大家详细介绍了VUE响应式原理的实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

  9. vue+Element ui实现照片墙效果

    这篇文章主要为大家详细介绍了vue+Element ui实现照片墙效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. vue+elemet实现表格手动合并行列

    这篇文章主要为大家详细介绍了vue+elemet实现表格手动合并行列,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部