axios在vue中的实践

421 阅读6分钟

首次入坑

你是否写过像我这样的代码?

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import axios from 'axios'
export default {
    methods:{
        getData(){
            axios.get('http://api.mozlee.com/getdata').then({data}=>{
                //todo bala bala~
            })
        },
        getData2(){
            axios.get('http://api.mozlee.com/getdata2').then({data}=>{
                //todo bala bala~
            })
        },
        getData3(){
            axios.get('http://api.mozlee.com/getdata3').then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

这个时候服务端小伙伴老王来了,告知你api.mozlee.com这个域即将下线,由api.jackma.com替换,并且将http协议全部更新到https协议。你怎么办?是不是只能一个一个去改axios请求的url?而且在修改的过程中十分繁琐,并且容易出现错改,漏改。机智的小伙伴已经想到用axios的baseURL去解决这个问题。所以我们改写一下这个组件。

改造一,使用baseURL

代码改写如下,这样解决上边老王提出来的问题。

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import axios from 'axios'
axios.baseURL = 'https://api.jackma.com'
export default {
    methods:{
        getData(){
            axios.get('/getdata').then({data}=>{
                //todo bala bala~
            })
        },
        getData2(){
            axios.get('/getdata2').then({data}=>{
                //todo bala bala~
            })
        },
        getData3(){
            axios.get('/getdata3').then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

可是老王这个孩子不省心啊,又过来告诉你我们的/getdata3接口,依然要用老的域名 api.mozlee.com,其他的接口保持 api.jackma.com。此时的你是不是想锤死老王?

改造二,使用create创建实例

面对老王的这次改动,我们来使用axios的create方法创建出不同的用于请求的实例。

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import axios from 'axios'
const mozleeAPI = axios.create({
    baseURL:'https://api.mozlee.com',
    ... // 其他config
})
const jackmaAPI = axios.create({
    baseURL:'https://api.jackma.com',
    ... // 其他config
})
axios.baseURL = 'https://api.jackma.com'
export default {
    methods:{
        getData(){
            jackmaAPI.get('/getdata').then({data}=>{
                //todo bala bala~
            })
        },
        getData2(){
            jackmaAPI.get('/getdata2').then({data}=>{
                //todo bala bala~
            })
        },
        getData3(){
            mozleeAPI.get('/getdata3').then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

此时的你总算舒了口气,完成了这个组件开发,去开发其他组件(功能),但是你发现,TMD这两个创建的请求实例在别的组件也要用到。

改造三,抽离axios相关实例。

此时的我们应该将请求相关的接口进行抽离,再向外部暴露。这样在其他组件就可以引入调用。 我们新建一个api.js

api.js的内容为

import axios from 'axios'

export const mozleeAPI = axios.create({
    baseURL:'https://api.mozlee.com',
    ... // 其他config
})
export const jackmaAPI = axios.create({
    baseURL:'https://api.jackma.com',
    ... // 其他config
})

组件A的内容为

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import {mozleeAPI,jackmaAPI} from './api.js'
export default {
    methods:{
        getData(){
            jackmaAPI.get('/getdata').then({data}=>{
                //todo bala bala~
            })
        },
        getData2(){
            jackmaAPI.get('/getdata2').then({data}=>{
                //todo bala bala~
            })
        },
        getData3(){
            mozleeAPI.get('/getdata3').then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

组件B的内容为

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
  </div>
</template>
<script>
import {jackmaAPI} from './api.js'
export default {
    methods:{
        getData(){
            jackmaAPI.get('/getdata').then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

到此,可算是解决了多组件复用axios实例的问题了。但是万恶的老王又TM来了。老王说,hey兄弟,我们的api.jackma.com这个域下的/getdata这个接口需要改一改,老板觉得/getdata有点丑,我们决定改成/getuser。此时你一边在心里问候着老王,一边窃喜,还好写的不多,只需要两个组件的内容。但是仔细一想,善变的老王不一定又会改什么,要是的API用多了岂不是和最开始使用axios出现了相同问题,容易错该,漏改。身为一个优秀的程序员,必须将其抽象。

改造四,将请求url抽象封装

api.js的内容为

import axios from 'axios'

const mozleeAPI = axios.create({
    baseURL:'https://api.mozlee.com',
    ... // 其他config
})

const jackmaAPI = axios.create({
    baseURL:'https://api.jackma.com',
    ... // 其他config
})
export getData(){
    return  jackmaAPI.get('/getuser')
}
export getData2(){
    return jackmaAPI.get('/getdata2')
}
export getData3(){
    return mozleeAPI.get('/getdata3')
}

组件A的内容为

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import { getData, getData2, getData3 } from './api.js'
export default {
    methods:{
        getData(){
            getData().then({data}=>{
                //todo bala bala~
            })
        },
        getData2(){
            getData2().then({data}=>{
                //todo bala bala~
            })
        },
        getData3(){
           getData3().then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

组件B的内容为

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
  </div>
</template>
<script>
import { getData } from './api.js'
export default {
    methods:{
        getData(){
            getData().then({data}=>{
                //todo bala bala~
            })
        }
    }
}
</script>

如此一来,看你老王还能玩出什么花活!

老王嘿嘿一笑,兄弟,我们的接口要求在api.mozlee.com域的url上带上一个时间time的参数,在api.jackma.com域的请求header中添加一个name:lilei的自定义头。

聪明的你当然不能再接口函数里边一个一个加了。

改造五,使用axios request拦截器

api.js的内容为

import axios from 'axios'

const mozleeAPI = axios.create({
    baseURL:'https://api.mozlee.com',
    ... // 其他config
})

mozleeAPI.interceptors.request.use(function (config) {
  config.url += `?time=${new Date().getTime()}`
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

const jackmaAPI = axios.create({
    baseURL:'https://api.jackma.com',
    ... // 其他config
})

jackmaAPI.interceptors.request.use(function (config) {
    config.header = Object.assign(config.header ,{'name':'lilei'})
    return config
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error)
})

export getData(){
    return jackmaAPI.get('/getuser')
}
export getData2(){
    return jackmaAPI.get('/getdata2')
}
export getData3(){
    return mozleeAPI.get('/getdata3')
}

这时候的老王确实提不出什么需求了,但是你发现了一个问题。api.jackma.com域的接口一定返回一种约定好格式的json 如下:

{
    data:['lilei','hanmeimei'],
    stat:1,
    msg:'success'
}

每次调用的时候都需要在Promise的then中做一个判断,当stat>0是做成功的事,stat!=0的时候做失败的事。

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import { getData, getData2, getData3 } from './api.js'
export default {
    methods:{
        getData(){
            getData().then({data}=>{
                if(data.stat>0){
                    doSuccess()
                }else{
                    doError()
                }
           }).catch(err=>{
              doError()
           })
        },
        getData2(){
            getData2().then({data}=>{
                if(data.stat>0){
                    doSuccess()
                }else{
                    doError()
                }
            }).catch(err=>{
                doError()
            })
        },
        getData3(){
           getData3().then({data}=>{
                if(data.status){
                    doSuccess()
                }else{
                    doError()
                }
           }).catch(err=>{
              doError()
           })
        }
    }
}
</script>

改造六、使用axios response拦截器。

import axios from 'axios'

const mozleeAPI = axios.create({
    baseURL:'https://api.mozlee.com',
    ... // 其他config
})

mozleeAPI.interceptors.request.use(function (config) {
  config.url += `?time=${new Date().getTime()}`
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

const jackmaAPI = axios.create({
    baseURL:'https://api.jackma.com',
    ... // 其他config
})

jackmaAPI.interceptors.request.use(function (config) {
    config.header = Object.assign(config.header ,{'name':'lilei'})
    return config
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error)
})

// response 拦截器
jackmaAPI.interceptors.response.use(function ({ data }) {
  // 对响应数据做点什么
  if (data.stat > 0) {
    return data
  } else {
    return Promise.reject(data)
  }
}, function (error) {
  console.log(error)
  return Promise.reject(error)
})

export getData(){
    return jackmaAPI.get('/getuser')
}
export getData2(){
    return jackmaAPI.get('/getdata2')
}
export getData3(){
    return mozleeAPI.get('/getdata3')
}

只要是非成功状态,不管是网络状态非200,还是200状态但是response结果的stat为0,我们都把他们reject。

这样我们在Promise.then中 只做Success相关,catch中只做错误相关。改造后的 组件代码如下

<template>
  <div id="app">
    <button @click="getData">加载数据</button>
    <button @click="getData2">加载数据2</button>
    <button @click="getData3">加载数据3</button>
  </div>
</template>
<script>
import { getData, getData2, getData3 } from './api.js'
export default {
    methods:{
        getData(){
            getData().then({data}=>{
                doSuccess()
           }).catch(err=>{
                doError()
           })
        },
        getData2(){
            getData2().then({data}=>{
                doSuccess()
            }).catch(err=>{
                doError()
            })
        },
        getData3(){
           getData3().then({data}=>{
                if(data.status){
                    doSuccess()
                }else{
                    doError()
                }
           }).catch(err=>{
              doError()
           })
        }
    }
}
</script>

哈哈,这下基本上可以满足老王的任性变动,以及自己开发的复用、简单化开发。

最后的建议

  • 一个项目多个域的接口,建议统一管理。建立一个统一的index.js管理不同的域,并且向外暴露axios实例。 index.js
import axios from 'axios'

const mozleeAPI = axios.create({
    baseURL:'//api.mozlee.com'
})
const jackmaAPI = axios.create({
    baseURL:'//api.jackma.com'
})
const hanmeimeiAPI = axios.create({
    baseURL:'//api.hanmeimei.com'
})

export {
    mozleeAPI, jackmaAPI,hanmeimeiAPI
}
  • 常见需求还有取消请求,可以使用axios相关API。