跌倒了也要記得爬起來繼續向前行

0%

Vue 的 axios 全域攔截器

Vue 的 axios 全域攔截器

本篇運用到 Vue Cli 以及 Vuex

以六角學院的 Vue 最終作業來說用到攔截器是為了搭配 Vue Loading Overlay 這個套件,平常在寫時執行每個 API 都會帶一次 truefalse ,用攔截器的好處是可以省去 API 數量 * 2 行的程式碼,如果 API 有 50 個這樣就可以省去 100 行的狀態控制程式碼,並且也不用煩惱在製作測試時狀態控制碼有沒有被關好。

起手式

安裝 vue-axios

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)

安裝 vue-loading-overlay

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
// methods 中的方法
getTest(){
this.$store.dispatch(setLoading, true); // 開始執行程式碼時打開 loading

this.axios.get(APIurl).then((res)=>{
console.log(res.data)
...
... //要執行的程式碼

this.$store.dispatch(setLoading, false); // 執行完程式碼後關閉 loading
});
}

像這樣的方式來控制 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
// methods 中的方法
getTest(){
this.axios.get(APIurl).then((res)=>{
console.log(res.data)
...
... //要執行的程式碼
});
}

只要是使用 this.axios 發動請求就會自動帶入 loading 的狀態控制囉!

進階 API 管理

當專案 API 越來越多的時候,資料也從各種不同的網域取得時,就會想要把 API 放在同一支檔案裡集中管理,目前是用 exportimport 的方式處理,在各個 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 提供了自訂義默認值的方法

1
axios.create({ });

目前我比較常使用 baseURL 和 timeout

1
2
3
4
5
6
axios.create({
// 引用 .env 所設定的網域
baseURL: process.env.VUE_APP_PATH,
// 單位毫秒,請求超過 10 秒時,就不再等待請求。
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 + '回應失敗')
}
);

// get API
export const get_user = () => {
return eventsRequest.get('/get_user/')
}

// post API
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: {
// get API
getTest() {
get_user().then((res) => {
console.log(res.data);
});
},
// post API
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);

結語

  • 設定好 axios 攔截器就可以省略掉很多行 loading 狀態控制碼。

  • 做好 API 管理,以後 API 路徑有變動時只要去 .env 修改網域位置或是進到 api.js 修改就 OK 了,就不用再檔案中一個一個去找散落的 API 字串。

參考:https://ykloveyxk.github.io/2017/02/25/axios%E5%85%A8%E6%94%BB%E7%95%A5/
範例 API:https://randomuser.me/