該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
我們在開發網頁的時候一定會處理到 RESTful API 的串接,絕大多數我們都會將 RESTful API 給寫在 component 裡面去做操作,但是這樣的做法在具有一定規模的平台上面是非常難以管理跟維護的。
在這邊我選用 axios
來作為我處理非同步的工具。
import { ref, onMounted } from "vue"
import axios from "axios"
export default {
setup() {
const Data = ref([]);
onMounted(()=> {
axios.get('https://test.api/api/item')
.then(res => {
Data.value = res;
})
.catch(error => {
console.log("handle error =>", error);
})
})
return {
Data
};
},
}
我這邊列出幾個常見到的 RESTful API 直接寫在 component 裡面的缺點:
headers
裡面塞入物件的時候,會重複的去寫這個物件,要改的時候會變很麻煩。所以實際上專案要管理 API,最後就是把 API 的部分給抽離出來,不要直接寫在裡面,你會說我會把前面的domain 拉出去變成變數或是集中把所有的 API 放入陣列裡面再去利用索引呼叫做管理,甚至可以統一拉到 Vuex去管理使用 API 等方法,但我都覺得這類的方法都不會是最佳管理 API 的最佳作法,相信我! 這類的管理的方式我做了許多的嘗試…,不同 domain 的 API 管理上面相對來說相對難度也會提高,除了程式可讀性以外,還要好維護,考量到許多因素後,我來分享一下我是怎麼做的。
首先開一個名字叫 api
的資料夾,裡面新增一個 index.js
,作為進入點,再來開始分類你的API。
我今天有一個部落格平台要做,然後有三個項目的功能要做,三個項目的 API 都各自散落在不同的機器,每個項目都有數個API 需要使用到。
// user.js
const userRequest = axios.create({
baseURL: 'https://api/user/'
})
export const postUserLogin = data => userRequest.post('/signIn', data)
export const postUserLogout = data => userRequest.post('/signOut', data)
export const postUserSignUp = data => userRequest.post('/signUp', data)
// article.js
const articleRequest = axios.create({
baseURL: 'https://api/article/'
})
export const getArticleItem = () => articleRequest.get('/ArticleItem')
export const postArticleMsg = data => articleRequest.post('/ArticleMsg', data)
export const postArticleLink = data => articleRequest.post('/ArticleLink', data)
// search.js
const searchRequest = axios.create({
baseURL: 'https://api/search/'
})
export const getSearch = data => searchRequest.get(`/Search?searchdata=${data}`)
export const getSearchType = () => searchRequest.get(`/SearchType`)
axios.create
去創造一個實體,再利用變數去接這個實體,get
或是post
,然後在 export
出去給外面的 js去 import
就好。再來我們在 api/index.js
整合這 3 個檔案的 API
import {
postUserLogin,
postUserLogout,
postUserSignUp
} from "./user.js"
import {
getArticleItem,
postArticleMsg,
postArticleLink
} from "./article.js"
import {
getSearch,
getSearchType
} from "./search.js"
export const apiPostUserLogin = postUserLogin
export const apiPostUserLogout = postUserLogout
export const apiPostUserSignUp = postUserSignUp
export const apiGetArticleItem = getArticleItem
export const apiPostArticleMsg = postArticleMsg
export const apiPostArticleLink = postArticleLink
export const apiGetSearch = getSearch
export const apiGetSearchType = getSearchType
接下來我們要使用的時候只要 import
api 這個資料夾就好了,像這樣就可以確保你 API 來源都是同一個進入點
import { apiGetArticleItem, apiGetSearch } from "../api";
實際在 component 內使用會像這樣。
import { apiGetArticleItem, apiGetSearch } from "../api"
import { ref, onMounted } from "vue"
export default {
setup() {
const getData = async () => {
try {
const item = await apiGetArticleItem()
const search = await apiGetSearch()
// 其他的處理
} catch (err) {
console.error(err)
}
}
onMounted(()=> {
getData()
})
return {}
},
}
這樣的方式一樣可以在 Vuex 或是透過 composition api 來引入使用。
import { apiGetArticleItem, apiGetSearch } from "../api"
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
item: [],
search: {},
}
},
actions: {
async getDate({commit}, newId) {
try {
const item = await apiGetArticleItem()
const search = await apiGetSearch()
commit("GET_DATA", { item: item.data, search: search.data })
} catch (err) {
console.error(err)
}
},
},
mutations: {
GET_DATA (state, payload) {
state.item = payload.item
state.search = payload.search
}
}
})
axios.create
所創造出來的實體你可以透過變數去重新給予這個實體一個新的名字,然後透過命名規則的方式來區分你的 api 來分類,在每個 import 的 js 檔案只要透過命名規則就可以清楚知道這個 api 目前是從哪個機器跟分類的。.then
還有.catch
等方法,使用 try catch
code會更簡潔。axios 有提供攔截 request 與 response 的方法,可以讓我們在發送 request 前或是 response 回來之後統一的去做一些處理,像是送出資料的時候檢查有沒有 token 或是資料回來了檢查API 是否有錯誤或是status code 是否不是 200 要做 Error handling。
我們可以在你 axios.create
那個檔案裡面這樣用,以 search.js
為例
// search.js
const searchRequest = axios.create({
baseURL: 'https://api/search/'
})
// 攔截 API request 的請求
searchRequest.interceptors.request.use(request=> {
// API送出前可以做最後的處理
request.headers['Authorization'] = "你的任何想塞進去的東西";
return request;
}, error=> {
// 如果送出前失敗了,這邊就可以做一些處理
return Promise.reject(error);
});
// 攔截 API response 的回傳
searchRequest.interceptors.response.use(response => {
// 這邊可以對回來的資料先進行驗證處理,再來決定要不要把資料給吐出去
return Promise.resolve(response);
}, error => {
// 這邊當API發生錯誤的時候就可以處理 Error handling
return Promise.reject(error.response.data);
})
export const getSearch = data => searchRequest.get(`/Search?searchdata=${data}`)
export const getSearchType = () => searchRequest.get(`/SearchType`)
我自己對於這樣的統一處理 Error handling 可以說是非常的喜歡,大家可以試試看。
Axios GitHub : https://github.com/axios/axios
之前我有在社群分享過我在規劃專案使用 Vuex 跟 API 的架構,裡面也有提到獨立管理 API 的部分,分享一下簡報給各位讀者當參考。
https://slides.com/mikecheng1208/deck-1
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng