前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue中axios的封装

vue中axios的封装

原创
作者头像
大龄老码农-昊然
修改2021-05-14 10:14:24
3.3K0
修改2021-05-14 10:14:24
举报

什么是 axios

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

特性

  • 从浏览器中创建 XMLHttpRequests
  • node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF

安装

使用npm:

代码语言:javascript
复制
 npm install axios

使用 bower:

代码语言:javascript
复制
bower install axios

使用 cdn:

代码语言:javascript
复制
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

关于content-type 类型

(1)application/x-www-form-urlencoded

首先,Content-Type被指定为 application/x-www-form-urlencoded;其次,提交的表单数据会转换为键值对并按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。 另外,如利用AJAX 提交数据时,也可使用这种方式。例如 jQuery,Content-Type 默认值都是”application/x-www-form-urlencoded;charset=utf-8”。

(2)multipart/form-data

另一个常见的 POST 数据提交的方式, Form 表单的 enctype 设置为multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符(这就是boundary的作用)分开,类似我们上面Content-Type中的例子。 由于这种方式将数据有很多部分,它既可以上传键值对,也可以上传文件,甚至多个文件。当上传的字段是文件时,会有Content-Type来说明文件类型;Content-disposition,用来说明字段的一些信息。每部分都是以 –boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(字段、文本或二进制等)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 –boundary– 标示结束。

(3)application/json

Content-Type: application/json 作为响应头比较常见。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串,其中一个好处就是JSON 格式支持比键值对复杂得多的结构化数据。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持JSON.stringify,服务端语言也都有处理 JSON 的函数,使用起来没有困难。 Google 的 AngularJS 中的 Ajax 功能,默认就是提交 JSON 字符串。

(4)text/xml

XML的作用不言而喻,用于传输和存储数据,它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据,在JSON出现之前是业界一大标准(当然现在也是),相比JSON的优缺点大家有兴趣可以上网search。因此,在POST提交数据时,xml类型也是不可缺少的一种,虽然一般场景上使用JSON可能更轻巧、灵活。

(5)binary (application/octet-stream)

在Chrome浏览器的Postman工具中,还可以看到”binary“这一类型,指的就是一些二进制文件类型。如application/pdf,指定了特定二进制文件的MIME类型。就像对于text文件类型若没有特定的子类型(subtype),就使用 text/plain。类似的,二进制文件没有特定或已知的 subtype,即使用 application/octet-stream,这是应用程序文件的默认值,一般很少直接使用 。

对于application/octet-stream,只能提交二进制,而且只能提交一个二进制,如果提交文件的话,只能提交一个文件,后台接收参数只能有一个,而且只能是流(或者字节数组)。

很多web服务器使用默认的 application/octet-stream 来发送未知类型。出于一些安全原因,对于这些资源浏览器不允许设置一些自定义默认操作,导致用户必须存储到本地以使用。一般来说,设置正确的MIME类型很重要。

简单封装

** 在src 目录下创建一个utils 工具文件夹,创建一个request.ts 文件。 ** request.ts 文件内容如下

代码语言:javascript
复制
import axios, { AxiosRequestConfig } from 'axios'

// 设置请求参数
const timeOut = 1000 * 1000 * 60
const protocol = location.protocol
const BaseUrl = "http://XXXX"
// 创建实例
const service = axios.create({
  baseURL: BaseUrl,
  timeout: timeOut,
})

// 添加请求拦截器
service.interceptors.request.use((config: any) => {
  config.headers = {'content-type': 'application/json'}
  return config
}), (error: any) => {
  return Promise.reject(error)
}

// 添加响应拦截器
service.interceptors.response.use((response: any) => {
  return response.data
}), (error: any) => {
  return Promise.reject(error)
}

//添加request 类
class request {
  // get 请求
  static get(url: any, params: any) {
    let config: AxiosRequestConfig = {
      method: 'get',
      params: params,
      url: url
    }
    return service(url, config)
  }
  // post 请求
  static post(url: any, params: any) {
    let config: AxiosRequestConfig = {
      method: 'post',
      data: params,
      url: url
    }
    return service(url, config)
  }
}

export default request

在src文件下创建api文件夹,用作api 的统一管理

代码语言:javascript
复制
import request from '../utils/request' 

export class TestService {
  test(params: any) {
    return request.get('/XXXX/XXX', params)
  }
}

在项目中调用

代码语言:javascript
复制
async handleTest() {
    let params = {
        id: 123
    }
    let result = await this.service.test(params)
    if (result) {
        console.log('result.data')
    }
}

那么,当我们封装好axios以后要如何去使用呢?接下来我们就一起去了解一下api接口的实现》》》》

实现创建一个js接口文件,统一管理接口。 然后在该文件中引入get()和post()

//api.js

import {get, post} from './http.js';

export const login = p => post('http://www.xxxx.com/api/', p)

在这里我们定义了一个login方法,这个方法有一个参数p,它是请求该接口时携带的参数对象。另一个url就是我们请求接口的地址。

这样,我们就写好了一个api接口了,接下来就是在页面方法中引用。

//login.vue

import {login} from '@/request/api';

export default {

name: 'login',

methods: {

onLogin() {

login({

userName: 123,

passWord: xxxx,

}).then(res = > {

console.log(res)

})

}

}

}

以上就是最基本的axios封装啦

补充封装的最佳实践 1.发送请求模块目录

2.@/api/url中存放的是每个模块的URL
代码语言:javascript
复制
// 商品模块 product.js
const product = {
  sku: {
    list: '/product/product/speclist',
    options: '/product/product/options'
  }
}
export default product

// 公用请求模块 common.js
const common = {
  region: {
    provinces: '/region/region/list',
    cities: '/region/region/list'
  },
  upload: {
    image: '/product/product/upload'
  }
}
export default common

发送请求的时候只需要指定key(sku/list),如:this.$ajax('sku/list', param);假定axios设置的baseURL是http://prod.storm.com/api/,那么最终的请求地址:http://prod.storm.com/api/product/product/speclist

3.requireURLs.js

使用webpack提供的require.context将src/api/url下后缀为js的所有文件引入,并整理出一个对象。

代码语言:javascript
复制
let urls = {}
const req = require.context('./url', false, /\.js$/)

const requireAll = requireContext => requireContext.keys().map(i => {
  let url = requireContext(i)
  Object.assign(urls, url.default)
})
requireAll(req)
export default urls

整合common.js & product.js,最终得到的对象如下:

代码语言:javascript
复制
urls = {
  sku: {
    list: '/product/product/speclist',
    options: '/product/product/options'
  },
  region: {
    provinces: '/region/region/list',
    cities: '/region/region/list'
  },
  upload: {
    image: '/product/product/upload'
  }
}
// 注意:模块名product和common没有了,所以需要注意命名的唯一,以免覆盖。
4.ajax.js
代码语言:javascript
复制
import axios from 'axios'
import qs from 'qs'
import jsd from 'js-file-download'
import store from '@/store'
import urlObj from './requireURLs'
import { Message, MessageBox } from 'element-ui'
import { getToken } from '@/utils/auth'

const service = axios.create({
  baseURL: `${process.env.BASE_API}/api/`, // 不同环境(dev/prod/test)使用不同的baseURL
  timeout: 5000
})
service.interceptors.request.use(
  config => {
    // 上传文件时,config.data的数据类型是FormData,
    // qs.stringify(FormData)的结果是空字符串,导致接口报**参数为空**的错误
    if (config.method === 'post' && config.data.constructor !== FormData) {
      config.data = qs.stringify(config.data)
    }
    if (store.getters.token) {
      config.headers.common['Auth-Token'] = getToken()
      // Auth-Token 登录过期后,重新登录不传token
      if (!config.headers.common['Auth-Token']) {
        delete config.headers.common['Auth-Token']
      }
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)
service.interceptors.response.use(
  response => {
    const res = response.data // 后台会把{ code, data, msg }返回到response.data中
    if (response.headers['content-type'].indexOf('application/vnd.ms-excel') !== -1) {
      // 通过请求接口下载Excel,由于后台返回的数据中(res)没有code(我也不知道为什么,反正后台就说没有),所以我只好通过响应头的content-type判断了
      // 下载接口返回的response header如下(后台把文件名放在Content-Disposition中(filename))
      // Content-Disposition: attachment; filename=20190323211209.csv
      // Content-Type: application/vnd.ms-excel;charset=GBK
      let fileName = response.headers['content-disposition'].split('=')[1]
      jsd(res, fileName)
      return
    }
    if (res.code === 0) { // 和后台约定code:0代表请求成功
      return res
    } else {
      if (res.code === 18500) { // 和后台约定code:18500代表token未过期,但是被更新了
        handleLogin('您已被登出,请重新登录')
      } else if (res.code === 18501) { // 和后台约定code:18500代表token过期
        handleLogin('登录已失效,请重新登录')
      } else {
        // 统一处理接口的报错信息,如果需要具体到页面去处理,可以和后台另外约定一个code
        Message({
          message: res.msg,
          type: 'error',
          duration: 3 * 1000
        })
      }
      return Promise.reject(res)
    }
  },
  error => {
    let data = error.response.data
    Message({
      message: data.msg,
      type: 'error',
      duration: 3 * 1000
    })
    return Promise.reject(data)
  }
)

const handleLogin = title => {
  MessageBox.confirm(title, '提示', {
    confirmButtonText: '重新登录',
    showCancelButton: false,
    showClose: false,
    type: 'warning'
  }).then(() => {
    store.dispatch('FedLogout').then(() => {
      location.reload()
    })
  })
}

const ajax = (path, data = {}, options = {}) => {
  // 如果path以http开头指定了一个url,直接用;否则就去遍历步骤3中的urls对象。如下:
  // this.$ajax('http://test.storm.com'),url = 'http://test.storm.com'
  // this.$ajax('sku/list'),url = '/product/product/speclist'
  let url = path.indexOf('http') === -1 ? path.split('/').reduce((o, k) => {
    return o[k]
  }, urlObj) : path
  let method = options.method || 'post'
  let params = { url, method }
  
  // 由于我司后台提供的接口大都是post和get,所以只考虑了这两种请求方式
  if (options.method === 'get') {
    Object.assign(params, { params: data }, options)
  } else {
    // post
    Object.assign(params, { data }, options)
  }
  return service(params)
}

export default ajax
5.在src/plugins中创建一个ajaxPlugin.js,并在src/main.js中引用
代码语言:javascript
复制
// ajaxPlugin.js
import ajax from '@/api/ajax'

let ajaxPlugin = {}

ajaxPlugin.install = Vue => {
  Vue.prototype.$ajax = ajax
}

export default ajaxPlugin

// main.js
import ajaxPlugin from '@/plugins/ajaxPlugin'

Vue.use(ajaxPlugin)
6.发送请求:

代码语言:javascript
复制
this.$ajax('sku/list').then(res => {})

参考文章

更多相关知识和参考文章来源可以关注我的博客站点

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 axios?
  • 特性
  • 安装
  • 关于content-type 类型
    • (1)application/x-www-form-urlencoded
      • (2)multipart/form-data
        • (3)application/json
          • (4)text/xml
            • (5)binary (application/octet-stream)
            • 简单封装
            • 那么,当我们封装好axios以后要如何去使用呢?接下来我们就一起去了解一下api接口的实现》》》》
            • 补充封装的最佳实践 1.发送请求模块目录
              • 2.@/api/url中存放的是每个模块的URL
                • 3.requireURLs.js
                  • 4.ajax.js
                    • 5.在src/plugins中创建一个ajaxPlugin.js,并在src/main.js中引用
                      • 6.发送请求:
                      • 参考文章
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                      http://www.vxiaotou.com