Axios 是以 Promise 為基礎去實現的 XHR AJAX,會開始使用它是因為 Vue 官方推薦使用它來取代原本的 vue-resource,而且自己也想學有別於 jquery.ajax() 的 AJAX 處理,順便練習 Promise 語法,便開始在最近的 Vue 專案上導入。

雖說現代的 XHR 替代方案 Fetch API 像是明日之星,但考量到瀏覽器支援與穩定度後仍選擇 axios 。也趁現在熟悉 Promise 語法,日後就能無痛接軌 Fetch 與 ES7 的 async,await

Fetch API的瀏覽器支援

Axios的瀏覽器支援

由於基本用法 github 的 readme 已經寫得很詳細,這邊只記一些比較特殊的技巧。

axios.create({config})

axios 可以創建 instance,我們可以針對不同的需求寫出個別的 axios 來用。例如寫一個需要驗證功能的 api,與不須驗證的公用 apiPublic。

api.js
1
2
3
4
5
6
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://auth.com',
//some authentication logic
})
export default instance;
apiPublic.js
1
2
3
4
5
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://public.com'
})
export default instance;

transformRequest

寫在 config 裡,只用在’PUT’,’POST’,’PATCH’時,用來在發出 request 前先對要傳的 data 做處理。要注意的是值必須是 array,裡面放 function。

什麼時候會用到?像是敝社用來驗證的 token 不放在 header 裡,而是跟資料塞一起丟給 server,就要像這樣處理:

1
2
3
4
5
transformRequest: [].concat(data => {
const token = Cookies.get('token');
const mergedData = $.extend(true, data, { token });
return mergedData;
}),

當然總是有更奇葩的需求,axios 預設傳的是 JSON,偏偏老舊的後端只吃 application/x-www-form-urlencoded,就還要再透過 parser 轉換成 query string。可以用 qs,npm 應該蠻多 package 都有依賴,我沒特別裝都能引用到。

1
2
3
4
5
6
7
8
import qs from 'qs'

// config...
transformRequest: [].concat(data => {
const token = Cookies.get('token');
const mergedData = $.extend(true, data, { token });
return qs.stringify(mergedData);
}),

Interceptors

transformRequest稍微不同,interceptors 可攔截 request 或 response,在then()catch()之前做些判斷。

例如 API 總是檢查登入狀態,會在錯誤時回傳的 JSON 包個 error: 'login failed'。我們只需檢查這個 key, 直接讓它 throw error 到 catch() 裡面進行後續處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const instance = axios.create({
//...
}).then(res => {
// success
}).catch(error => {
// failed
console.log(error);
});

instance.interceptors.response.use( response => {
const { data } = response;
if ( data.err ){
throw data.err;
}
});

檔案上傳

將要上傳的檔案轉成 Blob 物件,並使用 FormData API 格式傳輸,在這之前需要將 request header 的 Content-Type 設成 multipart/form-data

1
<input type="file" id="input" onchange="uploadFile(this.files)">
1
2
3
4
5
6
7
8
9
function uploadFile(files){
const selectedfile = files[0];
const fd = new FormData();
fd.append('file', new Blob([selectedFile]));
axios.post('/uploadFileAPI', fd , {
headers: { 'Content-Type': 'multipart/form-data' },
transformRequest: [(data, headers) => data], //預設值,不做任何轉換
});
}

看之後還有什麼想翻桌的需求解法,遇到再補上。

參考

從XHR到Fetch