還蠻不錯的想法。
簡述
以前因為我總認為 Axios 是個有點多餘的 library,因為不過就是把 fetch
包裝成 axios.get
或 axios.post
而已,那幹嘛不直接 fetch
就好了?
但一直到最近才發現原來有 Axios 有提供 instance(實體)的功能來讓你方便管理 API。實際用過以後覺得還不錯,所以來做個紀錄。
前置作業
等一下會提到幾個東西:
axios.create()
用來建立 instance
instance.interceptors
攔截器,用來處理發出 req 之前和拿到 res 之前要做的事
接著來邊看 code 邊解釋。
首先會先建立一個 axios 的實體:
附註:這邊用的測試 API 是這支。
1 2 3 4 5 6 7
| const instance = axios.create({ baseURL: "https://reqres.in/api/", headers: { 'Content-Type': 'application/json' }, timeout: 20000 })
|
建立好以後它就跟一般的 axios 一樣,可以用 instance.get
instance.post
之類的方法,不過在那之前我們會先設定「攔截器(interceptors)」。
攔截器就是讓你設定:
- request 發出去以前我想做什麼事?
- response 拿到以後,把值回傳給其他 handler 以前要做什麼?
所以 request 的部分會這樣設定:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
instance.interceptors.request.use( function (config) { config.headers.Authorization = 'Bearer 12345'; return config; }, function (err) { return Promise.reject(err); } )
|
而 response 的部分通常會著重在錯誤處理:
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
| instance.interceptors.response.use( function (res) { return res; }, function (err) { if (err.response) { switch (err.response.status) { case 404: console.log('不存在'); break case 500: console.log('伺服器錯誤'); break default: console.log(err.message); } } if (!window.navigator.onLine) { alert("你網路有問題哦!") return } return Promise.reject(err) } )
|
封裝
雖然上面做好後就可以直接用了,不過更好的方式是把他做封裝起來,讓我們在用的時候更直覺,像這樣:
1 2 3 4 5
| gerUserList({ page: 1 }).then(res => console.log('Get user list', res.data)) .catch(err => console.log('err', err))
|
經過包裝後就能很快知道我們要用哪隻 API,跟要帶的資料、參數是什麼。
不然原本的話得這樣寫:
1 2 3
| instance.get('/users/1') .then(res => console.log(res)) .catch(err => console.log('err', err))
|
所以第一步是先把 method 用 function 來包裝起來,讓它可以透過參數來決定要做什麼事:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| export function api (method, url, data = null, config) { method = method.toLowerCase(); switch (method) { case "post": return instance.post(url, data, config) case "get": return instance.get(url, { params: data }) case "delete": return instance.delete(url, { params: data }) case "put": return instance.put(url, data) case "patch": return instance.patch(url, data) default: console.log('unknown method type'); return false; } }
|
附註-關於 axios 的參數值
我原本不太懂上面的參數值為什麼可以那樣設,所以這邊才留個補充。如果你本來就看得懂的話請無視這一段。
來舉個例子,假設我想發一個 POST 請求,那我有兩種寫法。
第一種是用 post()
方法 + url
+ data
+ config
:
1 2 3 4 5 6 7 8 9
| axios.post("https://example.com/api/users", { name: "PeaNu", age: 20, des: "..." }, { params: { token: "123456" } })
|
第二種是全部塞在 config
:
1 2 3 4 5 6 7 8 9 10 11 12
| axios({ method: "post", url: 'https://aaa.com/api/users', data: { name: 'PeaNU', age: 20, des: "..." }, params: { token: "12345" } })
|
總之這兩個東西會是一樣的,只是寫法不同而已。
而上面用的 instance.post
正是第一種寫法,所以才要傳給他 url
+ data
+ config
這三個參數。
最後再補充一件事,就是 get()
跟 delete()
因為不需要傳資料,所以只會有 url
跟 config
這兩個參數,所以上面才會寫成 instance.delete(url, { params: data })
的形式。
如果怕忘記的話可以參考 vs code 的 TypeScript 提示
OK,解說就到這邊為止。
接著新增一個檔案,讓跟剛剛 export 出來的 function 在做一層包裝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { api } from "./api"; const URL = "/users";
export const gerUserList = (data) => { return api("GET", URL, data); }
export const addUser = (data) => { return api("POST", URL, data); }
export const putUser = (userId, data) => { return api("PUT", `${URL}/${userId}`, data); }
export const patchUser = (userId, data) => { return api("PATCH", `${URL}/${userId}`, data); }
export const deleteUser = (userId) => { return api("DELETE", `${URL}/${userId}`); }
|
或你要用 OOP 的方式包裝成 class
也可以:
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 { api } from "./api";
const URL = "/users";
export class UserApi { gerUserList = async (data) => { return api("GET", URL, data); } addUser = async (data) => { return api("POST", URL, data); } putUser = async (userId, data) => { return api("PUT", `${URL}/${userId}`, data); } patchUser = async (userId, data) => { return api("PATCH", `${URL}/${userId}`, data); } deleteUser = async (userId) => { return api("DELETE", `${URL}/${userId}`); } }
|
最後呢,就可以把我們寫好的東西拿來用了:
1 2 3 4 5 6 7
| import { gerUserList } from "./user";
gerUserList({ page: 1 }).then(res => console.log('Get user list', res.data)) .catch(err => console.log('err', err))
|
class 的寫法:
1 2 3 4 5 6 7
| import { UserApi } from "./userByClass"; const api = new UserApi();
api.gerUserList({ page: 1 }).then(res => console.log('Get user list', res.data)) .catch(err => console.log('err', err))
|
以上,有任何問題的話都可以到這邊參考原始碼。
參考資料