Vue 3 Pinia TypeScript
把 API 呼叫統一封裝進 services,Pinia store 才不會越養越肥
一開始的寫法
專案早期很多元件是這樣寫的:
// ArticleList.vue
const res = await axios.get(`/api/articles?page=${page.value}`);
articles.value = res.data.data;
Pinia store 裡也有幾乎一樣的邏輯,只是多包了 try/catch。結果同一支 API
被呼叫的地方,錯誤處理、loading 狀態、型別各寫一套,改一個欄位要改三個地方。
現在的分層
固定成三層,職責切乾淨:
services/articleService.ts:只負責打 API、回傳型別化的資料,不管畫面狀態。stores/article.ts(Pinia):呼叫 service,管理isLoading、error、快取好的資料。.vue元件:只呼叫 store 的 action 跟讀 state,不知道 axios 的存在。
// services/articleService.ts
export async function fetchArticles(page: number): Promise<Article[]> {
const { data } = await http.get<ArticleResponse>("/api/articles", {
params: { page },
});
return data.data;
}
// stores/article.ts
export const useArticleStore = defineStore("article", () => {
const articles = ref<Article[]>([]);
const isLoading = ref(false);
async function load(page: number) {
isLoading.value = true;
try {
articles.value = await fetchArticles(page);
} finally {
isLoading.value = false;
}
}
return { articles, isLoading, load };
});
換來的好處
換 API 版本或後端欄位改名的時候,只要動 services 那一層,store 跟元件完全
不用碰。單元測試也好寫很多,service 直接 mock 掉,store 的邏輯可以獨立測。
唯一的代價是一開始要多寫幾個檔案,小專案可能覺得多此一舉,但只要專案活過
半年,這筆帳一定划算。