Vue 3 實戰:打造高效能、可重複使用的 useFetch 非同步封裝
在現代前端開發中,處理 API 請求不只是「拿到數據」而已,更包含了 Loading 載入中、Error 錯誤處理 以及 數據響應性。今天我們示範如何結合 Axios 與 Composable,打造一個靈活且具備完整錯誤傳遞機制的 useFetch。
核心設計:封裝 Axios 與錯誤向上傳遞
在進行二次封裝時,最容易犯的錯誤就是「吃掉錯誤」。為了讓呼叫端(如元件)能根據錯誤進行後續處理(例如顯示通知),我們必須在 Axios 封裝層使用 throw err。
修改 utils/axios/api.js
確保當 API 發生異常時,錯誤能被正確拋出到 Composable 層。
js
// utils/axios/api.js
import instance from "./instance";
export default {
async GET(endPoint, config = {}) {
try {
const res = await instance.get(endPoint, config);
return res.data;
} catch (err) {
console.log("Axios 封裝方法失敗:", err);
throw err; // 重新拋出錯誤給上層處理
// return Promise.reject(err); // 也可以這樣寫
}
},
};實作 useFetch Composable
這個 Composable 的目標是:只要傳入 endPoint,就自動回傳數據、載入狀態與錯誤訊息。
js
// composables/useFetch.js
import api from "@/utils/axios/api";
import { ref } from "vue";
export default (endPoint, config = {}) => {
const data = ref(null);
const isLoading = ref(false);
const error = ref(null);
async function loadData() {
isLoading.value = true;
error.value = null;
try {
const res = await api.GET(endPoint, config);
if (res) data.value = res;
} catch (err) {
error.value = err;
} finally {
isLoading.value = false;
}
}
loadData();
return { data, error, isLoading };
};在元件中實戰應用:多 API 呼叫的命名藝術
當一個頁面需要同時呼叫多個 API(例如:使用者清單、產品清單)時,使用 ES6 解構賦值(Destructuring)並重新命名 是維持程式碼清晰度的最佳實踐。
vue
// App.vue
<script setup>
import useFetch from "@/composables/useFetch";
const {
data: users,
isLoading: usersIsLoading,
error: usersError,
} = useFetch("/users");
</script>
<template>
<div v-if="!usersIsLoading">
<h2>Users</h2>
<p v-if="usersError" v-text="usersError"></p>
<ul v-else-if="users?.length">
<li v-for="u in users" :key="u.id">
<pre v-text="u"></pre>
</li>
</ul>
<p v-else>No users data.</p>
</div>
<div v-else>Loading...</div>
</template>關鍵技術點解析
為什麼要用「解構重命名」?
在 useFetch 中,回傳的屬性名稱通常是固定的 data、isLoading、error。但在大型專案中,您會在同一個元件呼叫多個 Composable。透過 data: users 這種寫法,不僅解決了命名衝突,也增加了程式碼的 語意化。
錯誤處理的正確姿勢
許多開發者會在 Composable 裡直接 console.log 錯誤後就結束了。但正確的做法是更新 error 響應式變數,讓 UI 能夠根據錯誤狀態顯示對應的視窗或文字說明,這對使用者體驗(UX)至關重要。
