axios在vue中的實踐

瞅啥那發表於2019-12-01

首次入坑

你是否寫過像我這樣的程式碼?

<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。

相關文章