Vue 的 axios 全域攔截器
本篇運用到 Vue Cli 以及 Vuex
以六角學院的 Vue 最終作業來說用到攔截器是為了搭配 Vue Loading Overlay 這個套件,平常在寫時執行每個 API 都會帶一次 true
和 false
,用攔截器的好處是可以省去 API 數量 * 2
行的程式碼,如果 API 有 50 個這樣就可以省去 100 行的狀態控制程式碼,並且也不用煩惱在製作測試時狀態控制碼有沒有被關好。
起手式
1
| npm install --save axios vue-axios
|
main.js
寫入
1 2 3 4 5
| import Vue from 'vue' import axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios)
|
1
| npm install vue-loading-overlay --save
|
main.js
寫入
1 2 3 4
| import Loading from 'vue-loading-overlay' import 'vue-loading-overlay/dist/vue-loading.css'
Vue.component('Loading', Loading);
|
基本程式碼
通常我會很乾脆的放到 App.vue 裡面,可以省很多事,反正標題都寫全域了
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div id="app"> <Loading :active.sync="isLoading"></Loading> <router-view/> </div> </template>
<script> export default { computed: { isLoading() { return this.$store.state.isLoading; } }, } </script>
|
Vuex 的 store/index.js ,因為待會會用到所以先寫在這裡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({ state: { isLoading: false, }, actions: { setLoading(context, payload) { context.commit('Set_Loading', payload); } }, mutations: { Set_Loading(state, payload) { state.isLoading = payload; } },
})
|
變數名稱 isLoading
,控制 loading 畫面發動的時機,帶 Boolean : true
or false
大部分在寫程式碼的時候會在 axios 請求中帶入 isLoading 的控制碼片段
1 2 3 4 5 6 7 8 9 10 11 12
| getTest(){ this.$store.dispatch(setLoading, true); this.axios.get(APIurl).then((res)=>{ console.log(res.data) ... ... this.$store.dispatch(setLoading, false); }); }
|
像這樣的方式來控制 loading 畫面,如同前面所講的一個專案下如果有 50 個 API 要請,那就要寫 100 行控制碼來控制,作業感很重的感覺,下面來開始建立我們的 axios 攔截器吧。
建立攔截器
通常我會在 src
資料夾下面新增一個叫 JS 的資料夾,建立一個 axios.js 檔案,把剛剛在 store/index.js
寫好的狀態控制引進來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import axios from 'axios' import store from '@/store/index.js'
axios.interceptors.request.use( (confing) => { console.log('攔截請求'); store.commit('Set_Loading', true); return confing; }, (error) => { return Promise.reject(error); } );
axios.interceptors.response.use( (response) => { console.log('攔截回應'); store.commit('Set_Loading', false) return response; }, (error) => { return Promise.reject(error); } ); export default axios;
|
main.js
原本的
1
| import axios from 'axios'
|
改為
1
| import axios from '@/js/axios'
|
上面提到的 getTest();
就可以簡略為
1 2 3 4 5 6 7 8
| getTest(){ this.axios.get(APIurl).then((res)=>{ console.log(res.data) ... ... }); }
|
只要是使用 this.axios
發動請求就會自動帶入 loading 的狀態控制囉!
進階 API 管理
當專案 API 越來越多的時候,資料也從各種不同的網域取得時,就會想要把 API 放在同一支檔案裡集中管理,目前是用 export
和 import
的方式處理,在各個 vue 檔或是 store/index.js 我們只要引入變數就可以了。
變數引用
接下來一樣在 js 資料夾新增一個 api.js 的檔案。
寫入
1
| export const getRandomUser = 'https://randomuser.me/api/'
|
在 vue 檔引入
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> import { getRandomUser } from "@/js/api.js";
export default { methods: { getTest() { this.axios.get(getRandomUser).then((res) => { console.log(res.data); }); }, }, }; </script>
|
簡易的引用就完成了
不同網域的時候
當 API 網域不同時,我就會採用這種方式, axios 提供了自訂義默認值的方法
目前我比較常使用 baseURL 和 timeout
1 2 3 4 5 6
| axios.create({ baseURL: process.env.VUE_APP_PATH, timeout: 10000, });
|
綜合前面所使用的攔截器方法,來看完整的 api.js 內容吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import axios from 'axios'; import store from '@/store/index.js';
const eventsRequest = axios.create({ baseURL: process.env.VUE_APP_PATH, timeout: 10000, });
eventsRequest.interceptors.request.use( (config) => { console.log('攔截請求'); store.commit('Set_Loading', true); return config }, (err) => { console.log(err + '請求失敗') } );
eventsRequest.interceptors.response.use( (res) => { console.log('攔截回應'); store.commit('Set_Loading', false) return res }, (err) => { console.log(err + '回應失敗') } );
export const get_user = () => { return eventsRequest.get('/get_user/') }
export const post_login = (obj) => { return eventsRequest.post('/login/', obj) }
|
get API 實際上就等於
1
| axios.get(process.env.VUE_APP_PATH + '/get-user/');
|
post API 同理
1
| axios.post(process.env.VUE_APP_PATH + '/login/', obj);
|
vue 檔
把上面兩個變數引入到 vue 檔,就會是變數後面接 .then()
1
| get_user().then((res)=>{ });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> import { get_user, post_login } from "@/js/api.js";
export default { methods: { getTest() { get_user().then((res) => { console.log(res.data); }); }, postTest() { let obj = { a: 'abc'}; post_login(obj).then((res) => { console.log(res.data); }); }, }, }; </script>
|
這樣就 OK 哩。
補充
因為接收到請求就會立即執行關閉的狀態控制,
1
| store.commit('Set_Loading', false);
|
但有時候還有後續的程式碼要跑,使用者如果很急性的話 loading 一結束就按下功能鈕,可能會導致什麼錯誤也說不定,這邊我會再加上 setTimeout
延遲關閉的時間,留個緩衝的時間讓後續的程式碼跑完。
1
| setTimeout(()=>{store.commit('Set_Loading', false)}, 1000);
|
結語
參考:https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/
範例 API:https://randomuser.me/